【问题标题】:decorate a python class method with another class用另一个类装饰一个python类方法
【发布时间】:2021-12-26 13:02:55
【问题描述】:

我目前正在研究一个 Python 类,用作类方法的装饰器。 在这种情况下,我遇到了一个我很难理解的问题。 举个例子:

from functools import partial

class Decorator:
    def __init__(self, func = None, *args):
        self.uses_init = func is None
        self.func = func
        self.instance = None
        self.args = args
    
    def __call__(self, *args, **kwargs):
        self.func = args[0]
        
        def wrapper(cls, *args, **kwargs):
            print('before')
            parsed_func = self.func(cls, *args, **kwargs)
            print('after')
            return parsed_func
        
        return wrapper
    
    def call(self, cls, *args, **kwargs):
        print('before')
        parsed_func = self.func(cls, *args, **kwargs)
        print('after')
        return parsed_func       

    def __get__(self, instance, owner):
        # This is only used when uses_init == False
        return partial(self.call, instance)

可以不带参数使用@Decorator或带参数@Decorator(*args)

使用此代码按预期工作:

class HelloWorld:
    @Decorator()
    def print(self, name):
        print(name)
hello_world = HelloWorld()
hello_world.print("Max Musterman")
print('----------------------')
class HelloWorld:
    @Decorator
    def print(self, name):
        print(name)
hello_world = HelloWorld()
hello_world.print("Max Musterman")

我想使用self.call 方法代替wrapper 以避免重复代码。当我尝试这个时:

class Decorator:
    def __init__(self, func = None, *args):
        self.uses_init = func is None
        self.func = func
        self.instance = None
        self.args = args
    
    def __call__(self, *args, **kwargs):
        self.func = args[0]        
        return self.call
    
    def call(self, cls, *args, **kwargs):
        print('before')
        parsed_func = self.func(cls, *args, **kwargs)
        print('after')
        return parsed_func       

    def __get__(self, instance, owner):
        # This is only used when uses_init == False
        return partial(self.call, instance)

尽管检查了inspect.signature(Decorator().call)inspect.signature(Decorator()(lambda: None)) 方法的签名,但我遇到了TypeError: print() missing 1 required positional argument: 'name' 给出了相同的结果。

【问题讨论】:

    标签: python python-3.x decorator python-decorators


    【解决方案1】:

    装饰器无法获取参数。

    >>> class MyClass:
    ...     def __init__(self, func, *args):
    ...         print(args)
    ...
    >>> @MyClass
    ... def my_func():
    ...     pass
    ... 
    ()
    >>> @MyClass('args1', 'args2', 'args3')
    ... def my_func():
    ...     pass
    ... 
    ('args2', 'args3')
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    TypeError: MyClass object is not callable
    >>>
    

    您可以使用函数或方法来制作装饰器。
    在它之前“如何解决TypeError: MyClass object is not callable?”:

    >>> def my_decorator(*args **kwargs):
    ...     def func_getter(func):
    ...         return func(*args, **kwargs)
    ...     return func_getter  # mine decorator
    ... 
    >>> @my_decorator('arg1', 'arg2', 'arg3', kwarg1=1, kwarg2=2) # -> func_getter
    ... def print_row(*args, **kwargs):
    ...     print(args, kwargs)
    ... 
    ('arg1', 'arg2', 'arg3') {'kwarg1': 1, 'kwarg2': 2}
    

    解决方案一:使用函数制作装饰器:

    >>> class MyClass:
    ...     def __init__(self, func = None, *args): # copied form your code
    ...         ... # your code here
    ... 
    >>> def decorator(*args):
    ...     def func_getter(func):
    ...         return MyClass(func, *args)
    ...     return func_getter
    ... 
    >>> @decorator
    ... def test(): ...
    ... 
    >>> test
    <MyClass object at 0x0000000000000000>
    

    解决方案一:使用方法制作装饰器:

    >>> class MyClass:
    ...     def __init__(self, func = None, *args): # copied form your code
    ...         ... # your code here
    ...     @classmethod # romove this line in python3.10
    ...     def create(cls, *args):
    ...         def func_getter(func):
    ...             return cls(func, *args) # "cls" is "MyClass" instance
    ... 
    >>> @MyClass.create('args1', 'arg2', ...)
    ... def test(): ...
    ... 
    >>> test
    <MyClass object at 0x0000000000000000>
    

    【讨论】:

      【解决方案2】:

      感谢@Delta 的回答,我能够编写一个装饰器(-maker)来满足我的需求

      class Decorator:
          def __init__(self, func = None, text_before = "start", text_after = "end"):
              self.func = func
              self.text_before = text_before
              self.text_after = text_after
          
          def __call__(self, func):
              return self.create(func, text_before=self.text_before, text_after=self.text_after)
          
          @classmethod
          def create(cls, func, *args, **kwargs):
              return cls(func, *args, **kwargs)
          
          def call(self, cls, *args, **kwargs):
              print(f'--- {self.text_before} ---')
              parsed_func = self.func(cls, *args, **kwargs)
              print(f'--- {self.text_after} ---')
              return parsed_func       
      
          def __get__(self, instance, owner):
              return partial(self.call, instance)
      

      【讨论】:

        猜你喜欢
        • 2021-10-23
        • 2011-07-03
        • 1970-01-01
        • 1970-01-01
        • 2019-11-02
        • 2021-04-20
        • 2018-08-15
        • 2016-03-17
        • 2014-05-31
        相关资源
        最近更新 更多