我如何断言返回的函数源自外部函数?
def wraps(val):
def func():
return val
return func
a = wraps(5)
# assert that a comes from wraps()
我可以看到a.__qualname__
包括全局函数的名称:wraps.<locals>.func
,因此我可以基于它进行测试,但这是The Right Way™️吗?
作为上述要点的扩展,假设__qualname__
属性包含了封闭函数的名称,是否意味着对它的引用埋藏在某个地方,还是在生成该字符串时生成该字符串?函数是否已创建并针对该函数存储,并带有当时放弃的外部函数的引用?
答案 0 :(得分:4)
您无法执行此操作,因为在每个wraps
调用中都已构建了函数:
>>> a = wraps(1)
>>> b = wraps(1)
>>> a is b
False
>>> id(a)
139880648604800
>>> id(b)
139880648604936
解决方案是返回到实际的基本情况并将其放在外部并返回其中的partial
:
def func(val):
return val
from functools import partial
def wraps(val):
return partial(func, val)
这样,您就可以在任何需要的地方断言。
>>> f = wraps(10)
>>> f.func
<function func at 0x7f38805efd90>
>>> assert f.func is func
>>> assert f.func is not func
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
答案 1 :(得分:1)
专门解决__qualname__
的值是延迟生成的(因此必须保留对封闭函数的引用)还是预先设置,因此无需对封闭函数进行任何引用被保留:
PyObject* PyFunction_New(PyObject *code, PyObject *globals)
返回 值:新参考。返回与 代码对象代码。全局变量必须是具有全局变量的字典 该功能可以访问的变量。该函数的文档字符串和名称是从代码对象中检索的。
__module__
从全局变量中检索。参数默认值,注释和闭包设置为NULL。__qualname__
设置为 与函数名称相同的值。
PyObject* PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
返回值:新参考。作为PyFunction_New(),但还允许设置功能对象的__qualname__
属性。 qualname应该是unicode对象或NULL;如果为NULL,则__qualname__
属性设置为其与其相同的值__name__
属性。
这清楚地表明,__qualname__
的值在函数创建时被设置为unicode值,因此,在限定名称中存在作用域函数名称并不意味着存在对外部函数的引用保留。
答案 2 :(得分:0)
好吧,如果您以与可以肯定地断言的非关闭函数相同的方式定义关闭函数,
def wraps():
def func():
pass
return func
a = wraps
assert a is wraps
只能通过在外部定义func来实现上述目的
from functools import partial
def func(val):
return val
def wraps(val):
return partial(func, val)
a = wraps(10)
#Assertion is successful
#A can be called directly
print(a())
#10
#Assertion with qualname
assert a.func.__qualname__ == 'func'
在您最初的问题中,__qualname__
可以像这样用于断言
def wraps(val):
def func():
return val
return func
a = wraps(10)
assert a.__qualname__ == 'wraps.<locals>.func'
根据文档:https://docs.python.org/3/library/stdtypes.html#definition.qualname
上面的限定名称很有意义,因为它告诉我们func
内部有一个函数wraps
,对于它来说是本地的