如何“pythonically”动态构造闭包的命名空间

2024-09-24 22:17:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望动态地构造封闭环境的闭包名称空间,使闭包中的代码能够访问这个动态内容。下面是最简单的玩具例子,说明了我的问题:

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”将绑定变量“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”块有一个可变的值呢?例如,如果I change 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!

因此,由于上述问题,我需要的是一种方法,使绑定变量“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]:

Tags: textinreturn环境defevalargs动态
1条回答
网友
1楼 · 发布于 2024-09-24 22:17:42

在Python2或Python3中不能这样做闭包;不管您如何尝试execeval等等,Python编译器在closure的任何函数作用域中都看不到变量赋值这一事实意味着closure将在全局作用域中查找Y。你知道吗

您可以使用dis.dis来验证这一点,字节码将使用LOAD_GLOBAL来访问所有的“闭包”变量。你知道吗

您需要一次exec完成def f(z):...的全部代码。你知道吗


后两个例子似乎有效,但这一个:

def f(z):
    exec("Y="+str(z),locals())
    exec("def closure():\n return Y*2",locals())
    return eval("closure")

只使用locals()作为globalsexec——这同样不是正确的闭包。您正在编译绑定到自己的全局环境的新函数实例,而不是闭包。你知道吗

同样的代码也可以用更简单的方式编写

def f(z):
    globs = {'Y': z}
    exec('def closure():\n return Y*2', globs)
    return globs['closure']

相关问题 更多 >