BrightXiong 发表于 2023-4-2 19:40:14

类和对象-类装饰器-下

>>> # 用类做装饰器来装饰类
>>> 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

沐雨尘枫 发表于 2023-4-3 11:44:18

"链接:https://pan.baidu.com/s/1AcrWov-hEVa3n_JI6D3ung
提取码:7cia "
页: [1]
查看完整版本: 类和对象-类装饰器-下