# 类装饰器def report(cls):
def oncall(*args, **kwargs):
print('开始实例化对象')
_ = cls(*args, **kwargs)
print('实例化对象完成')
return _
return oncall
@report
class C:
pass
c = C()
# 开始实例化对象
# 实例化对象完成
@report
class C:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
print('构造函数被调用')
c = C(1, 2, 3)
# 开始实例化对象
# 构造函数被调用
# 实例化对象完成
print(c.__dict__)
# {'x': 1, 'y': 2, 'z': 3}
# 类装饰器的作用就是在类被实例化对象之前对其进行拦截和干预
# 让类做装饰器来装饰函数
class Counter:
def __init__(self):
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f'已经被调用了{self.count}次')
c = Counter()
c()# 已经被调用了1次
c()# 已经被调用了2次
c()# 已经被调用了3次
class Counter:
def __init__(self, func):
self.count = 0
self.func = func
def __call__(self, *args, **kwargs):
self.count += 1
print(f'已经被调用了{self.count}次')
return self.func(*args, **kwargs)
@Counter
def say_hi():
print('hi~')
print(say_hi)# <__main__.Counter object at 0x000001E41134AFD0>
# 函数say_hi已经被掉包成Counter的对象
say_hi()
# 已经被调用了1次
# hi~
say_hi()
# 已经被调用了2次
# hi~
say_hi()
# 已经被调用了3次
# hi~
# 用类做装饰器来装饰类
class Check:
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
self.obj = self.cls(*args, **kwargs)
return self
def __getattr__(self, item):
print(f'正在访问{item}....')
return getattr(self.obj, item)
@Check
class C:
def say_hi(self):
print(f'hi~ -> {self.name}')
def say_hey(self):
print(f'hey~ -> {self.name}')
# 将类C改写
@Check
class C:
def __init__(self, name):
self.name = name
def say_hi(self):
print(f'hi~ -> {self.name}')
def say_hey(self):
print(f'hey~ -> {self.name}')
c1 = C('c1')
c2 = C('c2')
print(c1.name)
# 正在访问name....
# c2
print(c2.name)
# 正在访问name....
# c2
c1.say_hi()
# 正在访问say_hi....
# hi~ -> c2
c2.say_hey()
# 正在访问say_hey....
# hey~ -> c2
# c1的name属性被c2的name属性覆盖
print(c1)# <__main__.Check object at 0x000001DB1AB5EF70>
print(c2)# <__main__.Check object at 0x000002A74A5DEF70>
# c1和c2其实是Check类的实例对象,其实传递给的是Check类的__call__()魔法方法
# 对象在@Check时就诞生了,class C在__call__()魔法方法中的第一个语句完成实例化,并将实例化对象传递给了self.obj属性,但返回的是self(Check类的对象自身,而非类C的对象)
# c1.name、c2.name实际上访问的是Check类对象的name属性,但Check没有name属性,那么他就会试图去查找__getattr__(self, name)这个魔法方法,Check中定义了此方法,返回的是类C的实例化对象
# Check仅在做装饰器(@Check)时被实例化了一次,那么c1 = C('c1')、c2 = C('c2')只是把对象当函数调用了两次,从而访问了两次相应的__call__()魔法方法,self.obj第一次保存的是name为c1的类C实例化对象,而第二次调用被name为c2的类实例化对象所覆盖
# 解决bug的方法:
def report(cls):
class Check:
def __init__(self, *args, **kwargs):
self.obj = cls(*args, **kwargs)
def __getattr__(self, item):
print(f'正在访问{item}....')
return getattr(self.obj, item)
return Check
@report
class C:
def __init__(self, name):
self.name = name
def say_hi(self):
print(f'hi~ -> {self.name}')
def say_hey(self):
print(f'hey~ -> {self.name}')
c1 = C('c1')
c2 = C('c2')
print(c1.name)
# 正在访问name....
# c2
print(c2.name)
# 正在访问name....
# c2
c1.say_hi()
# 正在访问say_hi....
# hi~ -> c1
c2.say_hey()
# 正在访问say_hey....
# hey~ -> c2
# 由于report返回的是Check类,被@report装饰过的class C其实是被替换为class Check,执行cx = C('cx')时就是在实例化Check类,调用构造函数__init__(),实例化report装饰器装饰过的类class C,然后把实例化的对象保存在self.obj属性中
# Check被实例化了两次,没有出现属性覆盖的bug
|