【问题标题】:Python - class decorator - runtime inheritancePython - 类装饰器 - 运行时继承
【发布时间】:2019-12-07 10:53:35
【问题描述】:

我正在尝试创建一个装饰器,它将向包装类__init__ 方法注入一些功能。这是有效的。

class Decorator:
    def __init__(self, arg):
        print(arg)
        self.arg = arg

    def __call__(self, cls):
        print(cls)

        class Wrapped(cls):
            def __init__(self, first_arg, second_arg, **kwargs):
                cls.__init__(self, first_arg, second_arg, **kwargs)
                print('in wrapped init', self.variable)

        return Wrapped


@Decorator('random_string')
class TestClass:
    def __init__(self, first_arg, second_arg, **kwargs):
        self.variable = 10
        print('TestClass init')


test = TestClass(first_arg='one', second_arg='two')

并产生

random_string
<class '__main__.TestClass'>
TestClass init
in wrapped init 10

由于一些神秘的原因,删除装饰器参数后代码不再工作(在这种情况下是随机字符串)

@Decorator
class TestClass:
    def __init__(self, first_arg, second_arg, **kwargs):
        self.variable = 10
        print('TestClass init')

输出:

Traceback (most recent call last):
  File "/home/python_examples/test_decorators.py", line 24, in <module>
    test = TestClass(first_arg='one', second_arg='two')
<class '__main__.TestClass'>
TypeError: __call__() got an unexpected keyword argument 'first_arg'

两个问题:

  • 这是一种众所周知且有效的装饰类的方法吗?
  • 为什么从未使用过的“random_string”参数至关重要?

【问题讨论】:

    标签: python class python-decorators


    【解决方案1】:

    这是因为您的装饰器类将arg 作为构造函数参数。因此,当您删除装饰器参数时,请确保您也已从 __init__ 方法中删除了该参数。改变这个

    class Decorator:
        def __init__(self, arg):
            print(arg)
            self.arg = arg
    
        def __call__(self, cls):
            print(cls)
    
            class Wrapped(cls):
                def __init__(self, first_arg, second_arg, **kwargs):
                    cls.__init__(self, first_arg, second_arg, **kwargs)
                    print('in wrapped init', self.variable)
    
            return Wrapped
    

    到这里

    class Decorator:
        def __init__(self):
            pass
    
        def __call__(self, cls):
            print(cls)
    
            class Wrapped(cls):
                def __init__(self, first_arg, second_arg, **kwargs):
                    cls.__init__(self, first_arg, second_arg, **kwargs)
                    print('in wrapped init', self.variable)
    
            return Wrapped
    

    会解决你的问题。

    【讨论】:

    • 它不起作用。回溯(最后一次调用):文件“/home/python_examples/test_decorators.py”,第 16 行,在 @Decorator TypeError: __init__() 接受 1 个位置参数,但给出了 2 个
    • 像这样Decorator()而不像这样Decorator调用装饰器。
    • 这两种方法有什么区别?在使用@注解时,我不必实例化装饰器。
    • 我的意思是,用@ 像@Decorator() 一样调用装饰器。如果是函数,则无需添加括号。由于它是一个类,我们需要添加括号。
    • “既然它是一个类,我们需要添加括号”,你是指装饰器本身还是它装饰的类(在这种情况下为 TestClass)?
    【解决方案2】:

    其实是这样的:

    # Assume you have a decorator class and a class named A which is needed to decorate.
    
    @Decorator
    class A:
        ...
    
    # is the same as 
    
    class A:
        ...
    
    A = Decorator()(A)
    

    这就是为什么你需要定义__cal__

    在这里你可以看到如果你的Decorator接受一些参数来初始化,你需要使用A = Decorator(xxx)(A)之类的东西。装饰器语法中的等价物是:

    @Decorator(xxx)
    class A:
        ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-23
      • 2011-03-01
      • 2014-02-02
      • 2022-01-18
      • 2011-11-20
      • 2014-03-07
      • 2018-04-17
      • 2011-09-18
      相关资源
      最近更新 更多