关于python:如何将参数传递给方法装饰器

How to pass an argument to a method decorator

我有一个这样的方法装饰师。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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')

这个很好用。但是,如果我想向decorator传递一个参数,该方法将不再作为参数传递,并且我得到以下错误:

TypeError: init() missing 1 required positional argument: 'f'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

如何将参数传递给decorator类,为什么它要重写该方法?


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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


将调用一个装饰器。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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__方法。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)

我对decorator工厂的命名方式与我对不同层次嵌套函数的明确命名方式相同,但是您当然应该使用一些对您的用例有实际意义的东西作为顶级名称。您可能还希望将其移出类命名空间,因为它可以调用EDOCX1的所有实例(可能有愚蠢的结果,因为它不打算是一个方法)。