执行、闭包和作用域

4
假设我想使用exec实现一个身份修饰符(即,对于没有参数的函数,它不应该执行任何操作)。当我尝试使用该修饰符定义闭包时,该作用域会在f_factory函数结束后保留并改变接下来的内容。 我希望理解为什么最后一个输出返回“1b”,而不是“1”。
def exec_identity(f):

    gl = globals()
    gl.update({'f':f})

    exec "def idfun(): return f()" in gl, locals()
    return idfun


class CallableClass(object):
    def __init__(self, s):
        self.s = s

    def make_callable(self):
        def f_factory(s):
            def f():
                print s

            return exec_identity(f)
            #return f
        return f_factory(self.s)




c1 = CallableClass("1")
f1 = c1.make_callable()
f1()
c1.s = "1b"
f1()
f1b = c1.make_callable()
f1b()
f1()

"""
Result:

1
1
1b
1b
"""

我知道如果我可以像这样保留exec语句,它就能按预期工作:

exec "def idfun(): return f()" in {'f':f}, locals()

请查看此链接:https://dev59.com/J3RC5IYBdhLWcg3wKtv2 - PasteBT
1个回答

3

这与以下事实有关:

gl = globals()
gl.update({'f':f})

在所有情况下,都是在同一个对象上工作。

因此,全局的f()被替换为一个新的函数,并被调用。旧的函数及其闭包将会丢失。

gl = dict(globals())
gl.update({'f':f})

通过复制 globals() 字典,可以避免这种情况的发生。


@Zah 已更新答案。 - glglgl
1
根据文档所述:“globals(): 返回一个表示当前全局符号表的字典。”但显然这并不意味着它是一个只读副本,而且它还可以修改实际的全局符号... - Zah
1
@Zah 对的。关于这一点,文档不太明确。“representing”可能意味着它已经是一个副本,或者它实际上返回全局符号表实际使用的字典(后者似乎是正确的)。作为普通的“dict”,它不能是只读的;因此你确实会修改真正的全局变量。 - glglgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接