用装饰器装饰 Python class class
Decorating a Python class with a decorator as a class
需要一些帮助 implement/understand 装饰器作为 class 如何在 Python 中工作。我发现的大多数示例要么装饰 class,但作为函数实现,或者作为 class 实现,但装饰函数。我的目标是创建实现为 classes 的装饰器并装饰 classes.
更具体地说,我想创建一个 @Logger
装饰器并在我的一些 class 中使用它。这个装饰器要做的就是简单地在 class 中注入一个 self.logger
属性,所以每次我用 @Logger
装饰一个 class 我就可以 self.logger.debug()
在其方法中。
一些初始问题:
- 装饰器的
__init__
接收什么参数?我只会接收装饰的 class 和一些最终的装饰器参数,这实际上是大多数情况下发生的情况,但请查看下面的 DOMElementFeatureExtractor
的输出。为什么它会收到所有这些参数?
__call__
方法呢?它会收到什么?
- 如何为装饰器提供参数 (
@Logger(x='y')
)?它会被传递给__init__
方法吗?
- 我真的应该在
__call__
方法中返回 class 的实例吗? (我唯一能让它工作的方法)
- 链接装饰器怎么样?如果前面的装饰器已经返回了 class 的实例,那将如何工作?我应该在下面的示例中修复什么才能
@Logger @Counter MyClass:
?
请查看此示例代码。我创建了一些虚拟示例,但最后您可以看到我的真实项目中的一些代码。
你可以在最后找到输出。
如能帮助理解 Python classes 装饰器作为 class 实现,我们将不胜感激。
谢谢
from abc import ABC, abstractmethod
class ConsoleLogger:
def __init__(self):
pass
def info(self, message):
print(f'INFO {message}')
def warning(self, message):
print(f'WARNING {message}')
def error(self, message):
print(f'ERROR {message}')
def debug(self, message):
print(f'DEBUG {message}')
class Logger(object):
""" Logger decorator, adds a 'logger' attribute to the class """
def __init__(self, cls, *args, **kwargs):
print(cls, *args, **kwargs)
self.cls = cls
def __call__(self, *args, **kwargs):
print(self.cls.__name__)
logger = ConsoleLogger()
setattr(self.cls, 'logger', logger)
return self.cls(*args, **kwargs)
class Counter(object):
""" Counter decorator, counts how many times a class has been instantiated """
count = 0
def __init__(self, cls, *args, **kwargs):
self.cls = cls
def __call__(self, *args, **kwargs):
count += 1
print(f'Class {self.cls} has been initialized {count} times')
return self.cls(*args, **kwargs)
@Logger
class A:
""" Simple class, no inheritance, no arguments in the constructor """
def __init__(self):
self.logger.info('Class A __init__()')
class B:
""" Parent class for B1 """
def __init__(self):
pass
@Logger
class B1(B):
""" Child class, still no arguments in the constructor """
def __init__(self):
super().__init__()
self.logger.info('Class B1 __init__()')
class C(ABC):
""" Abstract class """
def __init__(self):
super().__init__()
@abstractmethod
def do_something(self):
pass
@Logger
class C1(C):
""" Concrete class, implements C """
def __init__(self):
self.logger.info('Class C1 __init__()')
def do_something(self):
self.logger.info('something')
@Logger
class D:
""" Class receives parameter on intantiation """
def __init__(self, color):
self.color = color
self.logger.info('Class D __init__()')
self.logger.debug(f'color = {color}')
class AbstractGenerator(ABC):
def __init__(self):
super().__init__()
self.items = None
self.next_item = None
@abstractmethod
def __iter__(self):
pass
def __next__(self):
pass
def __len__(self):
pass
def __getitem__(self, key):
pass
class AbstractDOMElementExtractor(AbstractGenerator):
def __init__(self, parameters, content):
super().__init__()
self.parameters = parameters
self.content = content
@Logger
class DOMElementExtractor(AbstractDOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
def __iter__(self):
self.logger.debug('__iter__')
def __next__(self):
self.logger.debug('__next__')
def __len__(self):
self.logger.debug('__len__')
def __getitem__(self, key):
self.logger.debug('__getitem__')
class DOMElementFeatureExtractor(DOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
class DocumentProcessor:
def __init__(self):
self.dom_element_extractor = DOMElementExtractor(parameters={}, content='')
def process(self):
self.dom_element_extractor.__iter__()
a = A()
b1 = B1()
c1 = C1()
c1.do_something()
d = D(color='Blue')
document_processor = DocumentProcessor()
document_processor.process()
输出:
<class '__main__.A'>
<class '__main__.B1'>
<class '__main__.C1'>
<class '__main__.D'>
<class '__main__.DOMElementExtractor'>
DOMElementFeatureExtractor (<__main__.Logger object at 0x7fae27c26400>,) {'__module__': '__main__', '__qualname__': 'DOMElementFeatureExtractor', '__init__': <function DOMElementFeatureExtractor.__init__ at 0x7fae27c25840>, '__classcell__': <cell at 0x7fae27cf09d8: empty>}
A
INFO Class A __init__()
B1
INFO Class B1 __init__()
C1
INFO Class C1 __init__()
INFO something
D
INFO Class D __init__()
DEBUG color = Blue
DOMElementExtractor
DEBUG __iter__
不会是一个完整的答案,但我认为复习一下装饰器的基础知识会很有帮助。这是装修的样子:
@Logger
class A:
# A's code
根据定义,这相当于这样做:
class A
# A's code
A = Logger(A) # Logger has to be callable because...it's called
消息来源经常说装饰器“修改”,但这实际上只是预期用途。从技术上讲,您只需要 A
来定义(因此是函数、方法或 class)和 Logger
即可调用。如果 Logger
返回 "Hello, World"
,那就是 A
的结果。
好吧,让我们假设我们暂时没有装饰 A
并想一想 Logger(A)
需要什么才能“修改”。那么,A
是一个 class,你 调用 一个 class 来创建实例:A(*args)
。因此,Logger(A)(*args)
也必须是 A
的实例。但是 Logger(A)
不是 class A
,它是 Logger
的一个实例。幸运的是,您可以通过在其 class 中定义 __call__
方法来使实例可调用。 Logger
的 __call__
方法调用存储在其 cls
属性中的 class 和 returns 实例。
至于装饰器中的参数,考虑它等同于什么也有帮助。您有兴趣这样做:
@Logger(x='y')
class A:
# A code
所以它等同于:
class A:
# A code
A = Logger(x = 'y')(A)
注意 Logger
本身是 而不是 以 A
作为参数。它以 'y'
作为参数并返回 另一个以 A
作为参数的 可调用对象。因此,如果 Logger
是 class,Logger(x = 'y')
将是 Logger
实例。如果 class 有 __call__
方法,class 的实例也可以作为装饰器!
需要一些帮助 implement/understand 装饰器作为 class 如何在 Python 中工作。我发现的大多数示例要么装饰 class,但作为函数实现,或者作为 class 实现,但装饰函数。我的目标是创建实现为 classes 的装饰器并装饰 classes.
更具体地说,我想创建一个 @Logger
装饰器并在我的一些 class 中使用它。这个装饰器要做的就是简单地在 class 中注入一个 self.logger
属性,所以每次我用 @Logger
装饰一个 class 我就可以 self.logger.debug()
在其方法中。
一些初始问题:
- 装饰器的
__init__
接收什么参数?我只会接收装饰的 class 和一些最终的装饰器参数,这实际上是大多数情况下发生的情况,但请查看下面的DOMElementFeatureExtractor
的输出。为什么它会收到所有这些参数? __call__
方法呢?它会收到什么?- 如何为装饰器提供参数 (
@Logger(x='y')
)?它会被传递给__init__
方法吗? - 我真的应该在
__call__
方法中返回 class 的实例吗? (我唯一能让它工作的方法) - 链接装饰器怎么样?如果前面的装饰器已经返回了 class 的实例,那将如何工作?我应该在下面的示例中修复什么才能
@Logger @Counter MyClass:
?
请查看此示例代码。我创建了一些虚拟示例,但最后您可以看到我的真实项目中的一些代码。
你可以在最后找到输出。
如能帮助理解 Python classes 装饰器作为 class 实现,我们将不胜感激。
谢谢
from abc import ABC, abstractmethod
class ConsoleLogger:
def __init__(self):
pass
def info(self, message):
print(f'INFO {message}')
def warning(self, message):
print(f'WARNING {message}')
def error(self, message):
print(f'ERROR {message}')
def debug(self, message):
print(f'DEBUG {message}')
class Logger(object):
""" Logger decorator, adds a 'logger' attribute to the class """
def __init__(self, cls, *args, **kwargs):
print(cls, *args, **kwargs)
self.cls = cls
def __call__(self, *args, **kwargs):
print(self.cls.__name__)
logger = ConsoleLogger()
setattr(self.cls, 'logger', logger)
return self.cls(*args, **kwargs)
class Counter(object):
""" Counter decorator, counts how many times a class has been instantiated """
count = 0
def __init__(self, cls, *args, **kwargs):
self.cls = cls
def __call__(self, *args, **kwargs):
count += 1
print(f'Class {self.cls} has been initialized {count} times')
return self.cls(*args, **kwargs)
@Logger
class A:
""" Simple class, no inheritance, no arguments in the constructor """
def __init__(self):
self.logger.info('Class A __init__()')
class B:
""" Parent class for B1 """
def __init__(self):
pass
@Logger
class B1(B):
""" Child class, still no arguments in the constructor """
def __init__(self):
super().__init__()
self.logger.info('Class B1 __init__()')
class C(ABC):
""" Abstract class """
def __init__(self):
super().__init__()
@abstractmethod
def do_something(self):
pass
@Logger
class C1(C):
""" Concrete class, implements C """
def __init__(self):
self.logger.info('Class C1 __init__()')
def do_something(self):
self.logger.info('something')
@Logger
class D:
""" Class receives parameter on intantiation """
def __init__(self, color):
self.color = color
self.logger.info('Class D __init__()')
self.logger.debug(f'color = {color}')
class AbstractGenerator(ABC):
def __init__(self):
super().__init__()
self.items = None
self.next_item = None
@abstractmethod
def __iter__(self):
pass
def __next__(self):
pass
def __len__(self):
pass
def __getitem__(self, key):
pass
class AbstractDOMElementExtractor(AbstractGenerator):
def __init__(self, parameters, content):
super().__init__()
self.parameters = parameters
self.content = content
@Logger
class DOMElementExtractor(AbstractDOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
def __iter__(self):
self.logger.debug('__iter__')
def __next__(self):
self.logger.debug('__next__')
def __len__(self):
self.logger.debug('__len__')
def __getitem__(self, key):
self.logger.debug('__getitem__')
class DOMElementFeatureExtractor(DOMElementExtractor):
def __init__(self, parameters, content):
super().__init__(parameters, content)
class DocumentProcessor:
def __init__(self):
self.dom_element_extractor = DOMElementExtractor(parameters={}, content='')
def process(self):
self.dom_element_extractor.__iter__()
a = A()
b1 = B1()
c1 = C1()
c1.do_something()
d = D(color='Blue')
document_processor = DocumentProcessor()
document_processor.process()
输出:
<class '__main__.A'>
<class '__main__.B1'>
<class '__main__.C1'>
<class '__main__.D'>
<class '__main__.DOMElementExtractor'>
DOMElementFeatureExtractor (<__main__.Logger object at 0x7fae27c26400>,) {'__module__': '__main__', '__qualname__': 'DOMElementFeatureExtractor', '__init__': <function DOMElementFeatureExtractor.__init__ at 0x7fae27c25840>, '__classcell__': <cell at 0x7fae27cf09d8: empty>}
A
INFO Class A __init__()
B1
INFO Class B1 __init__()
C1
INFO Class C1 __init__()
INFO something
D
INFO Class D __init__()
DEBUG color = Blue
DOMElementExtractor
DEBUG __iter__
不会是一个完整的答案,但我认为复习一下装饰器的基础知识会很有帮助。这是装修的样子:
@Logger
class A:
# A's code
根据定义,这相当于这样做:
class A
# A's code
A = Logger(A) # Logger has to be callable because...it's called
消息来源经常说装饰器“修改”,但这实际上只是预期用途。从技术上讲,您只需要 A
来定义(因此是函数、方法或 class)和 Logger
即可调用。如果 Logger
返回 "Hello, World"
,那就是 A
的结果。
好吧,让我们假设我们暂时没有装饰 A
并想一想 Logger(A)
需要什么才能“修改”。那么,A
是一个 class,你 调用 一个 class 来创建实例:A(*args)
。因此,Logger(A)(*args)
也必须是 A
的实例。但是 Logger(A)
不是 class A
,它是 Logger
的一个实例。幸运的是,您可以通过在其 class 中定义 __call__
方法来使实例可调用。 Logger
的 __call__
方法调用存储在其 cls
属性中的 class 和 returns 实例。
至于装饰器中的参数,考虑它等同于什么也有帮助。您有兴趣这样做:
@Logger(x='y')
class A:
# A code
所以它等同于:
class A:
# A code
A = Logger(x = 'y')(A)
注意 Logger
本身是 而不是 以 A
作为参数。它以 'y'
作为参数并返回 另一个以 A
作为参数的 可调用对象。因此,如果 Logger
是 class,Logger(x = 'y')
将是 Logger
实例。如果 class 有 __call__
方法,class 的实例也可以作为装饰器!