【问题标题】:Exec, closures and scopes执行,闭包和范围
【发布时间】:2013-12-07 23:39:34
【问题描述】:

假设我想用 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()

【问题讨论】:

标签: python scope closures


【解决方案1】:

这与事实有关

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

在所有情况下都适用于同一个对象。

因此,全局f() 与一个新的交换,并被调用。旧的,连同它的关闭,都丢失了。

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

通过复制 globals() 字典来防止这种情况发生。

【讨论】:

  • @Zah 也更新了答案。
  • 根据文档:“globals():返回代表当前全局​​符号表的字典。”但显然这并不意味着它是一个只读副本,但它也修改了实际的全局符号......
  • @Zah 对。文档在这个问题上是模棱两可的。 “表示”可能意味着它已经是一个副本,或者它确实返回了全局 symol 表实际使用的 dicht(后者似乎是这种情况)。作为普通的dict,不能为只读;所以很明显你确实修改了真正的全局变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-07
  • 2013-02-17
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
相关资源
最近更新 更多