热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在Python中的“eval”中捕获的变量。-Capturedvariablesin“eval”inPython

Imhavingtroubleunderstandingthesemanticsofeval()andexecinPython.(Allcodeinthisq

I'm having trouble understanding the semantics of "eval()" and "exec" in Python. (All code in this question behaves the same way in Python 2.7.8 and Python 3.4.2). The documentation for "eval" says:

在Python中,我很难理解“eval()”和“exec”的语义。(这个问题中的所有代码在Python 2.7.8和Python 3.4.2中都是相同的方式)。“eval”的文档说明:

If both [locals and globals] are omitted, the expression is executed in the environment where eval() is called.

如果省略了(local和globals),则在调用eval()的环境中执行该表达式。

There is similar language for "exec". I clearly don't understand this sentence because I would expect the four functions defined by the following program to do the same thing.

“exec”也有类似的语言。我显然不理解这个句子,因为我期望下面的程序定义的四个函数也可以做同样的事情。

def h(x):
    ls = locals()
    exec('def i(y): return (w, x, y)', globals(), ls)
    i = ls['i']
    def       j(y): return (w, x, y)
    k = eval('lambda y: (w, x, y)')
    l =       lambda y: (w, x, y)
    return i, j, k, l

w = 1

i, j, k, l = h(2)

They do not.

他们不。

>>> i(3)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in i
NameError: name 'x' is not defined
>>> j(3)
(1, 2, 3)
>>> k(3)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in 
NameError: name 'x' is not defined
>>> l(3)
(1, 2, 3)

Disassembling the code reveals why: "x" is treated as a global variable by "eval" and "exec".

分解代码揭示了为什么:“x”被“eval”和“exec”当作一个全局变量。

from dis import dis
print("This is `i`:")
dis(i)
print("This is `j`:")
dis(j)
print("This is `k`:")
dis(k)
print("This is `l`:")
dis(l)
print("For reference, this is `h`:")
dis(h)

Output:

输出:

This is `i`:
  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
This is `j`:
 25           0 LOAD_GLOBAL              0 (w)
              3 LOAD_DEREF               0 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
This is `k`:
  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
This is `l`:
 27           0 LOAD_GLOBAL              0 (w)
              3 LOAD_DEREF               0 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
For reference, this is `h`:
 22           0 LOAD_NAME                0 (locals)
              3 CALL_FUNCTION            0
              6 STORE_FAST               1 (ls)

 23           9 LOAD_CONST               1 ('def i(y): return (w, x, y)')
             12 LOAD_NAME                1 (globals)
             15 CALL_FUNCTION            0
             18 LOAD_FAST                1 (ls)
             21 EXEC_STMT           

 24          22 LOAD_FAST                1 (ls)
             25 LOAD_CONST               2 ('i')
             28 BINARY_SUBSCR       
             29 STORE_FAST               2 (i)

 25          32 LOAD_CLOSURE             0 (x)
             35 BUILD_TUPLE              1
             38 LOAD_CONST               3 ()
             41 MAKE_CLOSURE             0
             44 STORE_FAST               3 (j)

 26          47 LOAD_NAME                2 (eval)
             50 LOAD_CONST               4 ('lambda y: (w, x, y)')
             53 CALL_FUNCTION            1
             56 STORE_FAST               4 (k)

 27          59 LOAD_CLOSURE             0 (x)
             62 BUILD_TUPLE              1
             65 LOAD_CONST               5 ( at 0x7ffc3843c3b0, file "test.py", line 27>)
             68 MAKE_CLOSURE             0
             71 STORE_FAST               5 (l)

 28          74 LOAD_FAST                2 (i)
             77 LOAD_FAST                3 (j)
             80 LOAD_FAST                4 (k)
             83 LOAD_FAST                5 (l)
             86 BUILD_TUPLE              4
             89 RETURN_VALUE

The question

"j" and "l" above have the behaviour I want. How can I get this behaviour using "eval" or "exec"?

“j”和“l”上面有我想要的行为。如何使用“eval”或“exec”来获得这种行为?

Failure 1

Using a class instead of a function as the outer wrapper does change the semantics, but in the opposite to the desired way. It makes "x" into a global.

使用类而不是函数作为外部包装器确实改变了语义,但与所期望的方式相反。它使“x”成为一个全局变量。

class H:
    x = 2
    f = staticmethod(eval('lambda y: (w, x, y)'))

H.dis(H.f)

w = 1
H.f(3)

Output:

输出:

  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in 
NameError: global name 'x' is not defined

Wrapping in "classmethod" or leaving it as an unbound instance method just makes things worse.

在“classmethod”中进行包装或将其作为未绑定的实例方法进行包装,只会使事情变得更糟。

Failure 2

Substituting "x" using string interpolation works for integers:

用字符串插值代替“x”的整数:

def h(x):
    return eval('lambda y: (w, %r, y)' % x)

k = h(2)

dis(k)

w = 1
k(3)

Output:

输出:

  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_CONST               1 (2)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
(1, 2, 3)

However, I don't want to assume that "x" can be losslessly converted to a string and back. The attempt is broken in the following examples:

但是,我不想假定“x”可以被无损地转换成字符串并返回。尝试在以下例子中被打破:

k = h(lambda: "something")

k = h(open('some_file', 'w'))

cell = ["Wrong value"]
k = h(cell)
cell[0] = "Right value"
k(3)

Failure 3

Since Python is looking for a global variable, one obvious attempt is to pass "x" as a global variable:

由于Python正在寻找一个全局变量,一个明显的尝试是将“x”作为一个全局变量传递:

def h(x):
    my_globals = {'w': w, 'x': x}
    return eval('lambda y: (w, x, y)', my_globals)

k = h(2)

dis(k)

w = 1
k(3)

Output:

输出:

  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE        
(1, 2, 3)

This attempt is broken because it reads the value of "w" too early:

这种尝试之所以失败,是因为它过早地读取了“w”的值:

w = "Wrong value"
k = h(2)
w = "Right value"
k(3)

Success 1

I did eventually find an approach that works, but I really don't like it:

我最终找到了一种可行的方法,但我真的不喜欢它:

def h(x):
    return eval('lambda x: lambda y: (w, x, y)')(x) 

k = h(2)

dis(k)

w = 1
k(3)

Output:

输出:

  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_DEREF               0 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE
(1, 2, 3)

In particular, this is going to become painful if I do not know the full list of local variables captured by the string I'm passing to "eval".

特别是,如果我不知道我传递给“eval”的字符串所捕获的局部变量的完整列表,这将会变得很痛苦。

Can you do better?

你能做得更好吗?

Update 2014-12-25

更新2014-12-25

Failure 4

Looking for more ways of creating the local variable "x", I tried this:

为了寻找创建局部变量x的更多方法,我尝试了以下方法:

def h(x):
    ls = locals()
    exec('x = x\ndef i(y): return (w, x, y)', globals(), ls)
    exec('_ = x\ndef j(y): return (w, x, y)', globals(), ls)
    return ls['i'], ls['j'], ls['_'], ls['x']

i, j, check1, check2 = h(2)

assert check1 == 2
assert check2 == 2

w = 1

print("This is `i`:")
dis(i)
print("This is `j`:")
dis(j)

print("i(3) = %r" % (i(3),))
print("j(3) = %r" % (j(3),))

The extra assignment to "x" has no effect. The assertions verify that "x" is in the dictionary of locals, but it is not captured by the lambdas. Here is the output:

额外分配给“x”没有效果。断言验证“x”在本地字典中,但它没有被lambdas捕获。这是输出:

This is `i`:
  2           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE
This is `j`:
  2           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE

The calls to "i" and "j" both crash, complaining that there is no global variable "x".

对“i”和“j”的调用都崩溃了,抱怨没有全局变量“x”。

Success 2

[Edit 2014-12-29: This succeeds only on Python 3.]

[编辑2014-12-29:只有在Python 3中才能成功。]

Another way of creating a local variable is like this:

创建局部变量的另一种方法是:

def h(x):
    i = eval('[lambda y: (w, x, y) for x in [x]][0]')
    j = eval('[lambda y: (w, x, y) for _ in [x]][0]')
    return i, j

i, j = h(2)

w = 1

print("This is `i`:")
dis(i)
print("This is `j`:")
dis(j)

print("i(3) = %r" % (i(3),))
print("j(3) = %r" % (j(3),))

Strangely, in this case the extra assignment to "x" does have an effect. This does work, i.e. "i" is different from "j". Here is the output:

奇怪的是,在这种情况下,“x”的额外赋值确实有效果。这工作,即。“i”与“j”不同。这是输出:

This is `i`:
  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_DEREF               0 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE
This is `j`:
  1           0 LOAD_GLOBAL              0 (w)
              3 LOAD_GLOBAL              1 (x)
              6 LOAD_FAST                0 (y)
              9 BUILD_TUPLE              3
             12 RETURN_VALUE
i(3) = (1, 2, 3)

The call to "j" crashes, complaining that there is no global "x", but "i" works as desired and has the correct bytecode.

调用“j”崩溃,抱怨没有全局的“x”,但是“i”按需要工作,并且有正确的字节码。

Why does this work, while "Failure 4" above does not? What is the rule that determines whether the local "x" can be captured? And what is the history of this design? (It seems absurd!)

为什么这个工作,而“失败4”却没有?决定本地“x”是否可以被捕获的规则是什么?这个设计的历史是什么?(看来荒谬的!)

2 个解决方案

#1


1  

I think you want your created functions to inherit the local environment of the function that creates them, but also the real global environment (of the function that creates them). That's why you don't like them referring to x as a global, right?

我认为您希望您创建的函数继承创建它们的函数的本地环境,但也需要真正的全局环境(创建它们的函数)。这就是为什么你不喜欢他们把x作为一个全局变量,对吧?

The following creates a "wrapper" function around the desired function, all within the same exec string. The values of the creating function's locals are passed in when you call or re-call the wrapper, creating a new wrapped closure.

下面创建一个围绕所需函数的“包装器”函数,所有这些函数都在同一个exec字符串中。当您调用或重新调用包装器时,创建函数的本地值会传入,从而创建一个新的包装闭包。

The code is sensitive to new variables being created in the locals context. It goes to some trouble to make sure that the function and wrapper names are both known and have values there.

代码对在本地环境中创建的新变量很敏感。要确保函数和包装器的名称都是已知的,并且在那里有值,这就有点麻烦了。

def wrap_f(code, gs, ls, wrapper_name, function_name):
    ls[function_name] = ls[wrapper_name] = None
    arglist = ",".join(ls.keys())
    wrapcode = """
def {wrapper_name}({arglist}):
{code}
    return {function_name}
    """.format(wrapper_name=wrapper_name, arglist=arglist, 
               code=code, function_name=function_name)
    exec(wrapcode, gs, ls)
    wrapper = ls[wrapper_name]
    return wrapper, wrapper(**ls)

So, to answer the original question, this code...

所以,为了回答最初的问题,这段代码…

def h(x):
    mcode = "    def m(y): return (w, x, y)"  # must be indented 4 spaces.
    mwrap, m = wrap_f(mcode, globals(), locals(), "mwrap", "m")
    return m

w = 1
m = h(2)
print m(3)

...produces this output:

…产生该输出:

(1, 2, 3)

And this example shows what to do when the locals in the creator function change:

这个例子展示了当创作者的功能改变时该怎么做:

def foo(x):
    barleycode = """
    def barley(y):
        print "barley's x =", x
        print "barley's y =", y
    """
    barleywrap, barley = wrap_f(barleycode, globals(), locals(), 
                               "barleywrap", "barley")
    barley("this string")
    print

    x = "modified x"
    barley = barleywrap(**locals())
    barley("new arg")
    print

    x = "re-modified x"
    barley("doesn't see the re-mod.")

x = "the global x"

foo("outer arg")

This produces the output:

这个生成的输出:

barley's x = outer arg
barley's y = this string

barley's x = modified x
barley's y = new arg

barley's x = modified x
barley's y = doesn't see the re-mod.

#2


0  

I'm not sure I quite got it myself, but I'll try my best: I guess when you run the eval/exec python does not understand that it was inside the function, I don't really know why. What I'll try to do is using format string like this

我不确定我自己是否有,但我会尽我最大的努力:我猜当你运行eval/exec python时,我不知道它在函数内部,我不知道为什么。我要做的是使用这样的格式字符串!

k = eval("lambda y: (w, {0}, y)".format(x))

I'm not sure if this thing works though. Also, why do you need to use eval and exec this way?

我不确定这个东西是否有用。另外,为什么需要使用eval和exec ?


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
author-avatar
捕风的yuhui_705
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有