Python:如何断言闭包的起源?

时间:2019-05-03 06:47:18

标签: python

我如何断言返回的函数源自外部函数?

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__属性包含了封闭函数的名称,是否意味着对它的引用埋藏在某个地方,还是在生成该字符串时生成该字符串?函数是否已创建并针对该函数存储,并带有当时放弃的外部函数的引用?

3 个答案:

答案 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,对于它来说是本地的