关于python:有条件地激活另一个装饰器的装饰器?

Decorator which conditionally activates another decorator?

我有一些函数,在正常情况下,通过用户输入提供的参数调用这些函数。但是,使用一系列参数调用其中一些函数是有效的,这些参数是在运行时根据某些系统状态确定的。

我希望用户能够有选择地指示我的程序使用所有有效输入调用这些函数,并返回每次调用的结果。我认为一个decorator类似于一个函数的激活开关,这个函数有另一个decorator,它指示要使用的参数系列会很好地工作。

另外,我需要保留函数签名和元数据。这对我的程序的运行至关重要。

这是我试过的,但不起作用。它基于这个例子。

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
27
28
29
30
31
32
33
34
35
36
37
>>> from decorator import decorator
>>> def the_list():
...     return ["foo","bar","baz"]
...
>>> import itertools
>>> @decorator
... def do_all(func):
...     # this will do nothing (besides wrap in a tuple) unless func is decorated with @gets_arg_from
...     if hasattr(func, 'get_from'):
...         return tuple(func(*args) for args in itertools.product(*(list_fun() for list_fun in func.get_from)))
...     else:
...         return (func(),)
...
>>> def gets_arg_from(*list_funcs):
...     # this will do nothing to func unless decorated with @do_all
...     def gets_arg_from(func, *args, **kwargs):
...         func.get_from = list_funcs
...         return func(*args, **kwargs)
...     return decorator(gets_arg_from)
...
>>> @gets_arg_from(the_list)
... def print_func(word):
...     # this will print its argument unless decorated with @do_all
...     # at that point it will print every element returned by the_list()
...     print word
...
>>> print_func("foo")
foo
>>> all = decorator(do_all, print_func)
>>> all()
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: print_func() takes exactly 1 argument (0 given)
>>> print_func.get_from
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'get_from'

我所期望的是:

1
2
>>> all()
("foo","bar","baz")

我注意到的是错误的:

  • gets_arg_from没有将get_from属性添加到func中。
  • 关于我使用@gets_arg_from(the_list)符号的一些内容是错误的。它认为我正试图通过两个论点(但为什么这会是一个问题?)
  • 至于我的动机,我之所以想到装饰器,是因为实际上有数百个这样的例程,它们的实现细节(以及它们的功能正确性)会经常发生变化,我不想使用inspect来根据它们的参数名来解释要做什么,也不想硬编码do_allfunc对每个函数都有意义。类方法可能有效,但就我而言,它们在语义上是人为设计的。此外,为了其他可能需要维护我的代码的人,我认为要求他们应用一个修饰器而不担心其余的,而不是使用某些参数名或将函数放在某个类或其他类中更容易。我意识到这个问题听起来很奇怪,所以我想这个脚注可能有助于让我看起来不像个疯子。


    这不是你想要的吗?

    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
    27
    28
    29
    30
    import functools
    from itertools import product

    def the_list():
        return ["foo","bar","baz"]


    def do_all(func):
        if hasattr(func, 'get_from'):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                return tuple(func(*args) for args in
                             product(*(lf() for lf in func.get_from)))
            return wrapper
        return func

    def gets_arg_from(*list_funcs):
        def decorator(func):
            func.get_from = list_funcs
            return func
        return decorator

    @gets_arg_from(the_list)
    def print_func(word):
        return word

    print print_func('foo')

    all = do_all(print_func)
    print all()

    编辑:解释

    这两个代码段是相同的:

    1
    2
    3
    @deco
    def func(...):
        some code

    1
    func = deco(lambda ...: some code)

    @有些东西只是函数调用和匿名函数创建的一种句法糖分…

    我将逐步解释下一个代码和平时期发生的事情:

    1
    2
    3
    @gets_arg_from(the_list)
    def print_func(word):
         return word

  • 第一个python创建一个异常函数,它接收一个参数word,并具有一个只返回该word的主体(或执行函数主体执行的任何操作)。

  • 然后调用函数get_arg_from,并将the_list作为参数传递给它。

  • get_arg_from创建decorator函数并返回它

  • get_arg_from返回的decorator函数被调用(这是句法上的糖化物),作为参数func传递在步骤1中创建的异常函数。

  • decorator只是将list_funcs元组赋给匿名函数的get_from属性,并返回异常函数。

  • decorator函数的返回值分配给一个变量print_func

  • 类似的效果可以通过以下方式实现:

    1
    2
    3
    4
    def __anonimous(word):
        return word
    __decorator = gets_arg_from(the_list)
    print_func = __decorator(__anonimous)

    所以基本上,gets_arg_from不是一个修饰器,它是一个返回修饰器的函数。

    另一方面,do_all是一个修饰器,它接收一个作为参数的函数,并返回原始函数(如果函数没有get_from属性)或替换原始函数(如果它有get_from属性)的wrapper函数。

    你可以在这里找到更多的例子。