|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
>>> # 用类做装饰器来装饰类
>>> 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_he(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 0x00F7E628>
>>> print(c2)
<__main__.Check object at 0x00F7E628>
>>>
# 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...
c1
>>> 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 |
|