|
发表于 2023-1-10 23:36:02
|
显示全部楼层
# 类装饰器
- 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
复制代码 |
|