我想动态构造闭包的封闭环境的命名空间,以便闭包内的代码可以访问此动态内容。这是说明我的问题的最简单的玩具示例:
def f():
exec("Y=7",locals())
def closure():
v=eval("Y*2")
return v
return closure
当我使用此代码时,会发生以下情况:
In [21]: Q = f()
In [22]: Q
Out[22]: <function __main__.closure>
In [23]: Q()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-23-51e72ed661f9> in <module>()
----> 1 Q()
<ipython-input-20-a47050c4450a> in closure()
3
4 def closure():
----> 5 return eval("Y*2")
6
7 return closure
<string> in <module>()
NameError: name 'Y' is not defined
因此,在这种情况下,使用“exec”将绑定(bind)变量“Y”添加到闭包的环境中不起作用。但是,由于嵌套函数,我也无法使用裸 exec。
我考虑使用 globals() 而不是 locals():
def f():
exec("Y=7",globals())
def closure():
return eval("Y*2")
return closure
## -- End pasted text --
In [27]: Q=f()
In [28]: Q
Out[28]: <function __main__.closure>
In [29]: Q()
Out[29]: 14
所以,这解决了我眼前的问题,但是如果我让“exec” block 具有可变值怎么办?例如,如果我更改 f() 为 Y 分配不同的值,我们就会遇到一个新问题:
def f(z):
exec("Y="+str(z),globals())
def closure():
return eval("Y*2")
return closure
## -- End pasted text --
In [33]: Q=f(2)
In [34]: Q
Out[34]: <function __main__.closure>
In [35]: Q()
Out[35]: 4
In [36]: G=f(4)
In [37]: G()
Out[37]: 8
In [38]: Q()
Out[38]: 8 #doh!
所以,我需要的是一种让绑定(bind)变量“Y”位于闭包/函数环境的命名空间中的方法,而不是位于“local()”(无论是什么)或“global()”中,由于上述问题。
似乎“exec”无法在此特定上下文中执行。我需要执行关闭本身吗?这似乎有效。
def f(z):
exec("Y="+str(z),locals())
exec("def closure():\n return Y*2",locals())
return eval("closure")
## -- End pasted text --
In [50]: G=f(3)
In [51]: G()
Out[51]: 6
In [52]: Q=f(5)
In [53]: Q()
Out[53]: 10
In [54]: G()
Out[54]: 6
In [55]: Q()
Out[55]: 10 #:-)
现在,如果我有一个更复杂的 Y 对象(例如函数)怎么办?
def f(z,args):
exec("Y= lambda " + args[0] + ","+args[1]+":"+args[0]+"*2+"+args[1]+"*3",locals())
exec("def closure(x,y):\n return z*Y(x,y)",locals())
return eval("closure")
## -- End pasted text --
In [64]: Q=f(2,['a','b'])
In [65]: Q(2,2)
Out[65]: 20
In [68]: G=f(3,['a','b'])
In [69]: G(2,-1)
Out[69]: 3
In [70]: Q(2,2)
Out[70]: 20
这似乎也有效。
但是,我想知道是否有一种更优雅的方法来动态构建闭包的环境。
我正在使用 IPython 2.7.11
Python 2.7.11 |Anaconda 2.4.0 (64-bit)| (default, Jan 19 2016, 12:08:31) [MSC v.1500 64 bit (AMD64)]
Type "copyright", "credits" or "license" for more information.
IPython 4.0.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: exec("x=2")
In [2]: x
Out[2]: 2
In [3]:
最佳答案
在 Python 2 或 3 中不能以这种方式执行闭包;无论你多么努力exec
, eval
因此,Python 编译器在 closure
的任何周围函数范围中都看不到变量赋值。意味着closure
将查找Y
相反,在全局范围内。
您可以使用dis.dis
来验证这一点,字节码将使用 LOAD_GLOBAL
s 访问所有“闭包”变量。
您需要exec
def f(z):...
的完整代码一口气。
后两个示例似乎有效,但这个:
def f(z):
exec("Y="+str(z),locals())
exec("def closure():\n return Y*2",locals())
return eval("closure")
只需使用 locals()
如globals
至exec
- 这又是没有适当的关闭。您正在编译绑定(bind)到自己的全局环境的新函数实例,而不是闭包。
相同的代码可以用更简单的方式编写:
def f(z):
globs = {'Y': z}
exec('def closure():\n return Y*2', globs)
return globs['closure']
关于python - 如何动态构造闭包的命名空间 "pythonically",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35926877/