繁体   English   中英

如何将参数传递给方法装饰器

[英]How to pass an argument to a method decorator

我有这样的方法装饰器。

class MyClass:
    def __init__(self):
        self.start = 0

    class Decorator:
        def __init__(self, f):
            self.f = f
            self.msg = msg

        def __get__(self, instance, _):
            def wrapper(test):
                print(self.msg)
                print(instance.start)    
                self.f(instance, test)
                return self.f
            return wrapper

    @Decorator
    def p1(self, sent):
        print(sent)

c = MyClass()
c.p1('test')

这很好用。 但是,如果我想将参数传递给装饰器,则该方法不再作为参数传递,并且我收到此错误:

TypeError: init ()缺少1个必需的位置参数:'f'

class MyClass:
    def __init__(self):
        self.start = 0

    class Decorator:
        def __init__(self, f, msg):
            self.f = f
            self.msg = msg

        def __get__(self, instance, _):
            def wrapper(test):
                print(self.msg)
                print(instance.start)    
                self.f(instance, test)
                return self.f
            return wrapper

    @Decorator(msg='p1')
    def p1(self, sent):
        print(sent)

    @Decorator(msg='p2')
    def p2(self, sent):
        print(sent)

如何将参数传递给装饰器类,为什么它会覆盖该方法?

调用装饰器。

在您的情况下,您将在__call__方法中接收函数作为参数

class MyClass:
    def __init__(self):
        self.start = 0

    class Decorator:
        def __init__(self, msg):
            self.msg = msg

        def __call__(self, f):
            self.f = f
            return self

        def __get__(self, instance, _):
            def wrapper(test):
                print(self.msg)
                self.f(instance, test)
                return self.f
            return wrapper

    @Decorator(msg='p1')
    def p1(self, sent):
        print(sent)

    @Decorator(msg='p2')
    def p2(self, sent):
        print(sent)

您的第一个示例有效,因为调用Class会创建一个实例,而该函数就是参数。

但在第二个示例中,您手动调用Class来设置msg参数,因此装饰过程会调用剩下的内容,即:实例和__call__方法。

描述符协议在这里没有多大用处。 您只需将函数本身传递给__call__并返回包装函数,而不会失去对实例的访问权限:

class MyClass:
    def __init__(self):
        self.start = 0

    class Decorator:
        def __init__(self, msg):
            self.msg = msg

        def __call__(self, f):
            def wrapper(instance, *args, **kwargs):
                print(self.msg)
                # access any other instance attributes
                return f(instance, *args, **kwargs)
            return wrapper

    @Decorator(msg='p1')
    def p1(self, sent):
        print(sent)

>>> c = MyClass()
>>> c.p1('test')
p1
test

当您使用参数调用装饰器时,您调用的函数实际上并不是作为装饰器本身工作。 相反,它是一个装饰工厂(一个函数或其他可调用的东西, return一个将充当装饰器的东西)。 通常,您可以通过添加额外级别的嵌套函数来解决此问题。 既然你用一个类来定义你的装饰器,那么直接做这个有点尴尬(虽然你可能会使它工作)。 但是,只要您在包装器函数中处理self (它现在将是MyClass的instance ,而不是Decorator类的实例),您的装饰器似乎并不需要成为一个类:

class MyClass:
    def __init__(self):
        self.start = 0

    def decorator_factory(msg):
        def decorator(f):
            def wrapper(self, test): # you might want to replace test with *args and **kwargs
                print(msg)
                print(self.start)
                return f(self, test)
            return wrapper
        return decorator

    @decorator_factory(msg='p1')
    def p1(self, sent):
        print(sent)

    @decorator_factory(msg='p2')
    def p2(self, sent):
        print(sent)

我按照我的方式命名装饰工厂,以明确嵌套函数的不同级别,但你当然应该使用对你的用例实际有意义的东西作为顶级名称。 您可能还希望将其移出类命名空间,因为它可用于调用MyClass所有实例(可能有愚蠢的结果,因为它不是一个方法)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM