python 装饰器中的 foo=bar(foo) 和 something=bar(foo) 有什么区别?
what is difference between foo=bar(foo) and something=bar(foo) in decorator in python?
我读到我们可以在 python 中创建任何函数的引用,但我也读到在创建装饰器时我们使用一种称为“@”的特殊语法:例如:@decorator_function
而这个 @decorator_function
等于 new_function=decorator_function(new_function)
所以我的疑问是:
anything = decorator_function(new_function)
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
,找到新函数
- 原函数调用新函数
- 新函数调用原函数...
你的两个作业都使用手动调用装饰器工作。但是其中之一(重新绑定 something
的那个)替换了原始函数,因此无法再通过其原始名称访问它。这与使用任何其他作业没有任何不同。例如:
def foo(x):
return x + 1
a = 10
a = foo(a)
当您将 foo(a)
的结果分配给 a
时,它会用新值 11
替换 10
的旧值。您再也无法获得 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
对于第一个,a = deco(something)
def deco(fn):
def wrapper(*args):
print('called')
return something(*args) # Notice here
return wrapper
第二个,something = deco(something)
是一样的除了你原来的函数something
现在变成了wrapper
函数deco
返回。
>>> something
<function deco.<locals>.wrapper at 0x7fbae4622f28>
>>> a
<function deco.<locals>.wrapper at 0x7fbae4622ea0>
something
和 a
在 原始 something
被 something = 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>
我读到我们可以在 python 中创建任何函数的引用,但我也读到在创建装饰器时我们使用一种称为“@”的特殊语法:例如:@decorator_function
而这个 @decorator_function
等于 new_function=decorator_function(new_function)
所以我的疑问是:
anything = decorator_function(new_function)
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
,找到新函数 - 原函数调用新函数
- 新函数调用原函数...
你的两个作业都使用手动调用装饰器工作。但是其中之一(重新绑定 something
的那个)替换了原始函数,因此无法再通过其原始名称访问它。这与使用任何其他作业没有任何不同。例如:
def foo(x):
return x + 1
a = 10
a = foo(a)
当您将 foo(a)
的结果分配给 a
时,它会用新值 11
替换 10
的旧值。您再也无法获得 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
对于第一个,a = deco(something)
def deco(fn):
def wrapper(*args):
print('called')
return something(*args) # Notice here
return wrapper
第二个,something = deco(something)
是一样的除了你原来的函数something
现在变成了wrapper
函数deco
返回。
>>> something
<function deco.<locals>.wrapper at 0x7fbae4622f28>
>>> a
<function deco.<locals>.wrapper at 0x7fbae4622ea0>
something
和 a
在 原始 something
被 something = 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>