【问题标题】:How to provide *args and **kwargs to __call__ using abbreviated notation of class based decorators?如何使用基于类的装饰器的缩写符号向 __call__ 提供 *args 和 **kwargs?
【发布时间】:2018-08-19 00:23:43
【问题描述】:

我有一个关于 python 中基于类的装饰器以及参数如何的问题 可以在装饰给定函数时提供。

考虑以下原型:

class MyDecoratorClass:
    def __init__(self, *args, **kwargs):
        # . . . Any code that is needed here
        pass

    def __call__(self, fun_to_decorate, *args, **kwargs):
        def inner(*args, **kwargs):
            # . . . Any code that is needed here
            return fun_to_decorate(*args, **kwargs)
        return inner

并考虑要装饰的功能:

def myfun(*args, **kwargs):
    print("myfun was called")

装饰上述功能的一种方法如下:

  • 第 1 步: 创建装饰器类的实例。这将调用 构造函数,即__init__。如果我通过任何论点 下面,当实例为 已创建。

    装饰器 = MyDecoratorClass()

  • 第 2 步:指定要修饰的函数,如果需要,提供 对应于*args 和/或**kwargs 的参数列表 __call__ 签名,因为它已在类中定义。

decorated_fun = decorator(myfun, arg1, arg2)

  • 第 3 步: 最后调用修饰函数,如果需要,提供一个 函数本身的参数列表,如果提供,则对应于 __call__ 函数内的内部函数的形参列表。 如果我理解正确的话,内部形参的命名空间不同于__call__形参的命名空间

    decorated_fun(...)

这行得通。但是,如果我想使用我拥有的缩写符号 写这样的东西:

@MyDecoratorClass(arg1, arg2)
def myfun(*args, **kwargs):
    print("myfun was called")

在上面的示例中,我传递了完全相同的两个位置参数arg1 和arg2。现在 问题是,这两个参数没有指定(*args, **kwargs) 在__call__ 中,但实际上是在__init__ 中指定的那些

所以我的问题是,当我们使用缩写符号来装饰 通过带有参数的基于类的装饰函数,如何访问 call 参数列表以便为每个修饰函数提供这些参数?

【问题讨论】:

    标签: python decorator python-decorators


    【解决方案1】:

    关于装饰器签名

    这样做...

    decorated_fun = decorator(myfun, arg1, arg2)
    

    ... 不是装饰器的工作方式。装饰器接受一个单个参数、一个函数并输出一个函数。

    特别是,上面的方法并不等同于正确的方法是this。

    @decorator(arg1, arg2)
    def myfun(...):
        ...
    
    # Which is actually equivalent to...
    myfun = decorator(arg1, arg2)(myfun)
    

    在__call__ 中访问__init__ 参数

    如果您想访问传递给__init__ 的参数,您可以将它们存储为实例属性。

    class MyDecoratorClass:
        def __init__(self, *args, **kwargs):
            # We store the arguments
            self.args = args
            self.kwargs = kwargs
    
            # Any other code that is needed here
    
    
        def __call__(self, fun_to_decorate): # Single argument
            def inner(*args, **kwargs):
    
                # Here you have access to self.args and self.kwargs
    
                return fun_to_decorate(*args, **kwargs)
    
            return inner
    

    对函数做同样的事情

    尽管请记住,您不需要使用类装饰器来完成此操作,但以下装饰器将具有相同的行为。

    def my_decorator_function(*args, **kwargs):
    
        def wrapper(decorated_func):
    
            def inner_wrapper(*inner_args, **inner_kwargs):
                # args and kwargs can be accessed here
                return decorated_func(*inner_args, **inner_kwargs)
    
            return inner_wrapper
    
        return wrapper
    
    @my_decorator_function(arg1, arg2)
    def myfun(*args, **kwargs):
        print("myfun was called")
    

    【讨论】:

    • 非常感谢您的详细描述。当你说:@decorator(arg1, arg2) def myfun(...): ... # 这实际上相当于... myfun = decorator(arg1, arg2)(myfun) 我不明白这个语法函数调用(我是初学者),函数调用的所有参数不应该像(a1,a2,......)吗?你建议的很有趣 (a1, a2) (...) 我不知道这个语法。
    • 这是因为在此示例中,decorator 是一个接受 arg1 和 arg2 的函数,并返回一个又接受 myfun 的函数。例如my_decorator_function。
    • 我明白了,所以 decorator(arg1, arg2) 本身变成了一个函数表达式(我的意思是它的结果),整个表达式 decorator(arg1, arg2) (我的乐趣)变成了最终的函数调用。对吗?
    • 正确!函数可以返回函数。
    猜你喜欢
    • 2018-08-28
    • 2015-03-24
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 2013-10-30
    • 2023-03-17
    • 2014-04-26
    相关资源
    最近更新 更多