Python 装饰器 类 with kwargs move function object

Python decorator classes with kwargs move function object

首先我想说我仍然是一个 python 学徒,所以我可能在这里遗漏了一些明显的东西,但是在研究了堆栈溢出和一些 google 文章之后我不能准确找到答案。

所以我的问题是,在 python 中,当你声明一个装饰器 class 时,取决于你是否使用 kwargs 调用它,你的 func 对象最终会出现在 init 方法上或调用方法。我知道问题的原因是,当装饰器没有 kwargs 时,它在装饰另一个函数时不会被调用,而只会被启动,但是当存在 kwargs 时,它会被启动和调用。但我觉得这种行为很奇怪,也许这就是 python 的工作方式。有一些变通办法,例如提到的 here,但它们仅是变通办法。正如我们的朋友 Raymond Hettinger 常说的那样:“一定有更好的方法!”

为了展示这个问题,我写了一个小脚本来说明如何在脚本开始之前调用带 kargs 的装饰器,而只有在调用函数本身时才调用不带 kwargs 的装饰器。这导致函数对象被解析为 __ call __ 方法,对于带有 kwargs 的装饰器,而它被传递给 __ init __ 方法,对于没有 kwargs 的装饰器。

下面是代码,看看我的意思:

class Decorator1():
def __init__(self, *a, **kw):
    print('\nInitializing Decorator1 Class')
    print('init 1 arguments = ' + str(a))
    print('init 1 karguments = ' + str(kw))
    self.func = a[0]

def __call__(self, *a, **kw):
    print('\nCalling Decorator1 Class')
    print('call 1 arguments = ' + str(a))
    print('call 1 karguments = ' + str(kw))
    return self.func()


class Decorator2():
    def __init__(self, *a, **kw):
        print('\nInitializing Decorator2 Class')
        print('init 2 arguments = ' + str(a))
        print('init 2 karguments = ' + str(kw))

    def __call__(self, *a, **kw):
        print('\nCalling Decorator2 Class')
        print('call 2 arguments = ' + str(a))
        print('call 2 karguments = ' + str(kw))
        return a[0]


@Decorator1
def my_function_1(height=2):
    print('This is my_function_1')
    return True

@Decorator2(test=3)
def my_function_2(height=2):
    print('This is my_function_2')
    return True


print('\n-------------------- Script Starts ----------------------------')
print('\n### Calling my_function_1 ###')
my_function_1(height=1)
print('\n### Calling my_function_2 ###\n')
my_function_2(height=2)
print('-------------------- Script Ends ------------------------------')

这是 bash 输出:

Initializing Decorator1 Class
init 1 arguments = (<function my_function_1 at 0x000001D575367F70>,)
init 1 karguments = {}

Initializing Decorator2 Class
init 2 arguments = ()
init 2 karguments = {'test': 3}

Calling Decorator2 Class
call 2 arguments = (<function my_function_2 at 0x000001D5753791F0>,)
call 2 karguments = {}

-------------------- Script Starts ----------------------------

### Calling my_function_1 ###

Calling Decorator1 Class
call 1 arguments = ()
call 1 karguments = {'height': 1}
my function 1

### Calling my_function_2 ###

my function 2
-------------------- Script Ends ------------------------------

编辑:

问题总结起来就是: 有没有一种简单的方法可以使用和不使用 kwargs 来调用同一个装饰器?我的意思是有没有一种方法可以让我像这样 (@Decorator1(kwarg1 = 3)) 为某些函数应用我的装饰器,同时也能像这样 (@Decorator1) 为其他函数应用它?

抱歉这么久post,希望大家帮我找到更好的方法:D

您对 Decorator1 的使用导致被修饰的函数作为参数传递给 Decorator1.__init__

您对 Decorator2 的使用会导致创建 Decorator2 的实例,然后 然后 被修饰的函数作为参数传递给 Decorator2.__call__.

装饰器语法是定义函数的快捷方式,那么

  1. 在该函数上调用装饰器
  2. 并将装饰器调用的结果绑定到函数的原始名称。

也就是下面的很不一样:

# Decorator1 is called on my_function_1
# Equivalent to my_function_1 = Decorator1(my_function_1)
@Decorator1
def my_function_1(height=2):
    print('This is my_function_1')
    return True

# An instance of Decorator1 is called on my_function_1.
# Equivalent to my_function_1 = Decorator1()(my_function_1)
@Decorator1()
def my_function_1(height=2):
    print('This is my_function_1')
    return True

在这个意义上,装饰器语法不同于 class 语句语法,其中

class Foo:
    ...

class Foo():
    ...

相同。

实际上,@ 之后的字符串一直是一个计算为可调用对象的表达式。从Python 3.9开始,字面上可以是任何这样的表达式,而不是像早期版本那样局限于点名或者点名调用。