提问人:chris 提问时间:5/3/2023 最后编辑:chris 更新时间:5/4/2023 访问量:71
Python 闭包/修饰函数的变量赋值 -
Python closures / variable assignment of decorated functions -
问:
我正在尝试学习 python 中的闭包和装饰器。
我知道,在我下面的代码中,变量 fn 已被分配给一个“cell”对象,一旦该对象作为参数传递给函数“outer”并调用 outer,它本身将引用函数对象。my_func
我不明白为什么,当从函数内部调用(修饰函数)时,它会调用(即修饰函数)而不是“原始”未修饰的my_func函数(这是作为参数传递给“外部”的参数)fn
inner
inner
我没有预料到装饰会导致我上面提到的单元格现在引用 的“不同版本”,从控制台输出中显示的是发生的事情。my_func
my_func
因此,我对下面的代码有两个问题:
- 在作业中,作业左手边的“my_func”是什么?我知道这是一个名为“my_func”的变量,它掩盖了函数(但被定义为的原始函数仍然存在)。
my_func = outer(my_func)
my_func
my_func
- 为什么现在似乎是参考,而不是通过的。也许它实际上并没有这样做,我误解了一些事情。
fn
inner
my_func
任何帮助将不胜感激。
我编写了以下代码:
def my_func():
print(f"This function's name is '{my_func.__name__}'. Its ID is {hex(id(my_func))}")
return None
def outer(fn):
def inner():
print(f"The function that is about to be called is called '{fn.__name__}'. Its ID is {hex(id(fn))}")
return fn()
return inner
print(f"I have created a function called '{my_func.__name__}'. Its ID is {hex(id(my_func))}")
my_func = outer(my_func)
print(f"The function has been decorated. The variable 'my_func' refers to a function object called '{my_func.__name__}'. Its ID is {hex(id(my_func))}\n")
my_func()
以下内容将打印到控制台:
I have created a function called 'my_func'. Its ID is 0x7f3c040cbb50
The function has been decorated. The variable 'my_func' refers to a function object called 'inner'. Its ID is 0x7f3c040f4040
The function that is about to be called is called 'my_func'. Its ID is 0x7f3c040cbb50
This function's name is 'inner'. Its ID is 0x7f3c040f4040
我所期待的是:
I have created a function called 'my_func'. Its ID is 0x7f3c040cbb50
The function has been decorated. The variable 'my_func' refers to a function object called 'inner'. Its ID is 0x7f3c040f4040
The function that is about to be called is called 'my_func'. Its ID is 0x7f3c040cbb50
The function that is about to be called is called 'my_func'. Its ID is 0x7f3c040cbb50
我承认这是“问题”的原因,但我不明白为什么。我原以为闭合 fn 会继续引用“原始”未修饰的功能。我不明白为什么赋值似乎会“改变”fn 引用的对象。my_func = outer(my_func)
my_func
my_func
outer(my_func)
答:
当您修饰函数时,它被函数替换。my_func
inner
装修后,有.__my_func__.name == "inner"
若要调整函数名称,请使用 functools.wraps 修饰器。 允许将修饰函数的元数据传输到封闭函数。functools.wraps
def outer(fn):
@functools.wraps(fn)
def inner():
print(...)
return fn()
return inner
更多解释:
在声明中:
def my_func():
print(f"Name is '{my_func.__name__}'. ID is {hex(id(my_func))}")
return None
创建一个模块变量,该变量包含对函数对象的引用。 引用此模块变量。每次执行函数时,都会解析指向的引用。my_func
{my_func.__name__}
my_func
my_func = outer(my_func)
在此之后,变量包含对新创建的函数的引用。举例来说,该函数已被内部函数所取代。my_func
inner
my_func
创建内部函数时,它捕获引用(闭包)作为局部变量。所指的引用永远不会改变,并将永远保持原来的功能。my_func
fn
fn
每次调用时,它都会创建一个新的唯一函数。outer
inner
执行最后一次打印时:
print(f"Name is {my_func.__name__}...")
my_func
引用新函数 所以是 。my_func.__name__
"inner"
在下面的示例中,我们可以看到由 changes 和 new 函数托管的引用捕获了对旧函数的引用:my_func
>>> def my_func():
... pass
...
>>> hex(id(my_func))
'0x7ffb218c0430'
>>> my_func = outer(my_func)
>>> hex(id(my_func))
'0x7ffb218c0310'
>>> my_func.__closure__
(<cell at 0x7ffb218b7c10: function object at 0x7ffb218c0430>,)
评论
def my_func
my_func
my_func
my_func
my_func
inspect.stack()[0].function
评论
inner
my_func
inner