简体   繁体   English

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

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

I have a method decorator like this. 我有这样的方法装饰器。

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')

This works fine. 这很好用。 However, If I want to pass an argument to the decorator, the method is no longer passed as an argument, and I get this error: 但是,如果我想将参数传递给装饰器,则该方法不再作为参数传递,并且我收到此错误:

TypeError: init () missing 1 required positional argument: 'f' 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)

How do I pass an argument to the decorator class, and why is it overriding the method? 如何将参数传递给装饰器类,为什么它会覆盖该方法?

A decorator will be called . 调用装饰器。

In your case you receive the function as a parameter in the __call__ method 在您的情况下,您将在__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)

Your first example works because calling the Class creates an instance and the function is the parameter. 您的第一个示例有效,因为调用Class会创建一个实例,而该函数就是参数。

But in your second example you call the Class manually to set the msg parameter, so you the decoration process calls what's left, ie: the instance and that goes to the __call__ method. 但在第二个示例中,您手动调用Class来设置msg参数,因此装饰过程会调用剩下的内容,即:实例和__call__方法。

The descriptor protocol doesn't serve much of a purpose here. 描述符协议在这里没有多大用处。 You can simply pass the function itself to __call__ and return the wrapper function without losing access to the instance: 您只需将函数本身传递给__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

When you call a decorator with arguments, the function you call isn't actually working as a decorator itself. 当您使用参数调用装饰器时,您调用的函数实际上并不是作为装饰器本身工作。 Rather, it's a decorator factory (a function or other callable that return s something that will act as the decorator). 相反,它是一个装饰工厂(一个函数或其他可调用的东西, return一个将充当装饰器的东西)。 Usually you solve this by adding an extra level of nested functions. 通常,您可以通过添加额外级别的嵌套函数来解决此问题。 Since you're defining your decorator with a class, that's a bit awkward to do directly (though you probably could make it work). 既然你用一个类来定义你的装饰器,那么直接做这个有点尴尬(虽然你可能会使它工作)。 But there doesn't really seem to be any need for your decorator to be a class, as long as you handle self in the wrapper function (it will be the instance of MyClass now, rather than an instance of a Decorator class): 但是,只要您在包装器函数中处理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)

I named the decorator factory the way I did to be explicit about the different levels of nested functions, but you should of course use something that's actually meaningful for your use case as the top level name. 我按照我的方式命名装饰工厂,以明确嵌套函数的不同级别,但你当然应该使用对你的用例实际有意义的东西作为顶级名称。 You might also want to move it out of the class namespace, since it will be available to call on all instances of MyClass (with possibly silly results, since it's not intended to be a method). 您可能还希望将其移出类命名空间,因为它可用于调用MyClass所有实例(可能有愚蠢的结果,因为它不是一个方法)。

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

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