【发布时间】:2021-07-19 22:49:21
【问题描述】:
需要一些帮助来理解装饰器的行为...
这里有一些代码:
import random
class MyDecorator(object):
""" Logger decorator, adds a 'logger' attribute to the class """
def __init__(self, *args, **kwargs):
print(random.randint(1, 100), *args, **kwargs)
self.cls = args[0]
def __call__(self, *args, **kwargs):
# Do something, inject some attribute
setattr(self.cls, 'x', 1234)
# Return an instance of the class
return self.cls(*args[1:], **kwargs)
@MyDecorator
class A:
def __init__(self):
print(f'A {self.x}')
@MyDecorator
class B:
""" Parent class """
def __init__(self):
print(f'B {self.x}')
class B1(B):
""" Child class """
def __init__(self):
super().__init__()
self.logger.info('Class B1 __init__()')
# Here the decorator is applied directly to the class that is going to be instantiated
# Decorator's __init__() receives the class as arg[0], so I can store it and use it when __call__()'ed
a = A()
# Here the decorator is not applied to the class being instantiated, rather to its parent class
# It looks like the decorator's __init__() is being called twice:
# - first time it do recceives the class to which it is applied (in this case, B)
# - second time it receives 3 arguments: a string containing the name of the child class, a tuple containing an instance of the decorator class itself and then some dict containing internal Python controls (I think)
b1 = B1()
输出:
82 <class '__main__.A'>
47 <class '__main__.B'>
52 B1 (<__main__.MyDecorator object at 0x7fd4ea9b0860>,) {'__module__': '__main__', '__qualname__': 'B1', '__doc__': ' Child class ', '__init__': <function B1.__init__ at 0x7fd4e9785a60>, '__classcell__': <cell at 0x7fd4eaa0f828: empty>}
A 1234
Traceback (most recent call last):
File "main.py", line 39, in <module>
b1 = B1()
File "main.py", line 12, in __call__
setattr(self.cls, 'x', 1234)
AttributeError: 'str' object has no attribute 'x'
所以,我的问题是:
- 当我将装饰器应用于父类而不是其子类时会发生什么?看起来装饰器正在为父/子调用,并且在每种情况下都传递了不同的参数集
- 在这种情况下,我将如何解决“通过装饰器进行类实例化”? (一切都在像 A 这样的情况下正常工作,其中装饰器直接应用于正在实例化的类)
- 我真的应该返回类的实例吗?如果我需要链接一些装饰器会发生什么?在这种情况下,我的
__call__方法应该是什么样子
@MyDecorator1
@MyDecorator2
class A():
感谢您的帮助!
【问题讨论】:
-
这里的问题是你已经用
MyDecorator的实例替换了A和B类 -
根据How to Ask,请一次只问一个问题。除了多个问题之外,您的代码还有很多问题,只是被您的代码同时执行多个不相关但不完整的事情所掩盖。
-
正式地,应用装饰器是在评估
class或def语句后执行的调用,装饰器的结果替换语句的结果。这就是@所做的一切。实例化、继承、调用实例化转发、参数集、类作为装饰器等所有恶作剧都使事情变得复杂。您尝试做或测试的一件事是什么? -
您的
MyDecorator实例似乎被用作元类...不知道为什么。 -
@F.F.Knob 真的,不要使用基于类的装饰器。使用基于函数的,例如
def Logger(cls): cls.logger = logger然后确保return cls。问题是,当您执行@MyDecorator时,它会实例化MyDecorator的一个实例,这就是分配给名称A和B的内容,所以当您执行class B1(B): ...时,然后B不是B类,它是MyDecorator的一个实例。当您从非类继承时会发生奇怪的事情
标签: python decorator python-decorators metaclass