如果作为参数传递给装饰器,装饰器将函数返回为 none

问题描述 投票:0回答:1

我一直在尝试了解装饰器,我发现了这个奇怪的小东西,假设我们有一个像这样的装饰器

import functools
global_event_registration =[]

def handler_all(func):
    if(func):
        global_event_registration.append(func)
    return func

它只是将函数添加到全局列表并返回它,如果我们运行该函数,我们会得到预期的输出

@handler_all
def print_yay():
    print('yay!')
print(global_event_registration)
print_yay()

输出:

[<function __main__.print_yay()>]
yay!

但是如果我们更改

handler_all
以接受任意参数,比如说
count
,然后再次运行相同的操作,则会出错,

def handler_all(func, count=2):
    if(func):
        global_event_registration.append(func)
    return func

@handler_all(count=2)
def print_yay():
 print('yay')

它会错误地指出缺少位置论证

func
,我的问题是因为在第一种情况下,
func
是隐式传递的,为什么第二种情况不是这样?

我还发现了这个stackoverflow线程,它使用

functools.partial

来处理它
def handler_all(func=None, count=2):
    if not func:
        return functools.partial(handler_all, count=count)
    if(func):
        func()
        global_event_registration.append(func)
    return func

我得到了线程的答案,我们只是返回一个部分函数,该函数已隐式传递给它

func 
但同样的问题仍然存在,这是如何将函数传递给它而不是根本不使用它?为什么它在第二种情况下隐式传递,而不是在第一种情况下,因为函数定义是相同的?

还有一个奇怪的事情是,

在这个例子中

def test(name="your_name"):
    def decorator(func):
        print(name)
        func()
        return func
    return decorator

@test(name='altair')
def print_yay():
    print('yay')

当你运行这个时,输出是

altair
yay

我的问题是为什么?我们只是返回对内部函数的引用而不是调用它,那么为什么它会被调用呢?

如果我们将 func 移至外部函数,它会按预期工作,没有输出。

def test(func):
    def decorator():
        func()
        return func
    return decorator

@test
def print_yay():
    print('yay')

同样的细微差别,如果我定义一个像这样的装饰器并显式调用内部函数,这反过来应该返回传递给它的函数,但是当我运行它时它返回一个

NoneType
,又是为什么?

def test(func=None,name="your_name"):
    def decorator(func):
        print(name)
        func()
        return func
    return decorator(func)

输出是

altair

<function print_yay at 0x7fcf60466440>

新行由装饰器返回,这意味着我们作为

print
传入的
func
工作正常。 但如果我打电话给
print_yay
,它只会说
NoneObject is not callable

@test(func=print, name='altair')
def print_yay():
    print('yay')

print_yay()

输出:

      1 @test(func=print,name='altair')
      2 def print_yay():
      3     print('yay')
----> 5 print_yay()

TypeError: 'NoneType' object is not callable

有人可以向我解释这些细微差别吗?我很难弄清楚幕后到底发生了什么,而且我根本找不到任何资源可以解释这一点。

python-3.x python-decorators
1个回答
0
投票

因为:

@decorator(some_arg)
def foo():
    pass

相当于:

def foo():
    pass

foo = decorator(some_arg)(foo)

所以只关注这个例子:

def handler_all(func, count=2):
    if(func):
        global_event_registration.append(func)
    return func

@handler_all(count=2)
def print_yay():
     print('yay')

所以,这就是正在发生的事情:

>>> def handler_all(func, count=2):
...     if(func):
...         global_event_registration.append(func)
...     return func
...
>>> @handler_all(count=2)
... def print_yay():
...      print('yay')
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: handler_all() missing 1 required positional argument: 'func'
>>> def print_yay():
...     print("yay")
...
>>> print_yay = handler_all(count=2)(print_yay)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: handler_all() missing 1 required positional argument: 'func'
>>>

正如你所说,它出错是因为它缺少一个参数。发生的事情是因为 你在糖发生变化以传递任何东西之前调用装饰器

© www.soinside.com 2019 - 2024. All rights reserved.