在 Python 的装饰器中,foo=bar(foo) 和 something=bar(foo) 有什么区别?



我读到我们可以在python中创建任何函数的引用,但我也读到在创建装饰器时我们使用一个名为"@"的特殊语法:例如:@decorator_function 而这个@decorator_function等于new_function=decorator_function(new_function)

所以我的怀疑在我看来是:

  1. anything = decorator_function(new_function)
  2. new_function=decorator_function(new_function)

两者都扮演着闭合的角色,但都产生了不同的输出。 那么它们之间的最大区别是什么?

示例代码:

def deco(fn):
def wrapper(*args):
print('called')
return fn(*args)
return wrapper

def something(x):
if x == 0:
print('first')
something(x+1)
else:
print('hello')

print('new name')
a = deco(something)
a(0)
print('nreassigning to the same name')
something = deco(something)
something(0)

你写的原始something函数对something进行递归调用,而不是a

如果将deco(something)分配给a,则something仍然是原始函数,递归调用将调用原始函数:

  • 新函数调用原始函数
  • 原始函数查找something,查找原始函数
  • 原始函数
  • 调用原始函数...

如果将deco(something)分配给something,则something现在是新函数,递归调用将调用新函数:

  • 新函数调用原始函数
  • 原始函数查找something,查找新功能
  • 原始函数调用新函数
  • 新函数调用原始函数...

对于第一个,a = deco(something)

def deco(fn):
def wrapper(*args):
print('called') 
return something(*args)      # Notice here
return wrapper 

第二个,除了你原来的函数something现在已经成为deco返回的wrapper函数之外,something = deco(something)是一样的。

>>> something
<function deco.<locals>.wrapper at 0x7fbae4622f28>
>>> a
<function deco.<locals>.wrapper at 0x7fbae4622ea0>

somethinga都会在原始somethingsomething = deco(something)分配覆盖之前对其进行包装。Python 在内部将原始something函数存储在包装函数中的某个位置:

>>> something.__closure__[0].cell_contents
<function something at 0x7fbae4622bf8>
>>> a.__closure__[0].cell_contents
<function something at 0x7fbae4622bf8>

在上一个作业中,something变得不同了:

>>> something
<function deco.<locals>.wrapper at 0x7fbae4622f28>

使用手动调用装饰器的两个作业都有效。但是其中一个(重新绑定something的那个(替换了原始函数,因此无法再通过其原始名称访问它。这与使用任何其他作业没有任何不同。例如:

def foo(x):
return x + 1
a = 10
a = foo(a)

foo(a)的结果分配给a时,它会将旧的10值替换为新值11。你不能再得到10了。

装饰器语法也做同样的事情。

def deco(fn):
def wrapper(*args):
print('called')
return fn(*args)
return wrapper
def func_1(x):
pass
func_1 = deco(func_1) # replace the old func_1 with a decorated version
@deco                 # the @ syntax does the same thing!
def func_2(x):
pass

不禁止使用装饰器来创建不同名称的函数,它通常没有那么有用(因此没有特殊的语法(:

def func_3(x):
pass
func_4 = deco(func_3) # this works, creating a new function name without hiding the old one

最新更新