热门标签 | 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 ?


推荐阅读
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 当使用 `new` 表达式(即通过 `new` 动态创建对象)时,会发生两件事:首先,内存被分配用于存储新对象;其次,该对象的构造函数被调用以初始化对象。为了确保资源管理的一致性和避免内存泄漏,建议在使用 `new` 和 `delete` 时保持形式一致。例如,如果使用 `new[]` 分配数组,则应使用 `delete[]` 来释放内存;同样,如果使用 `new` 分配单个对象,则应使用 `delete` 来释放内存。这种一致性有助于防止常见的编程错误,提高代码的健壮性和可维护性。 ... [详细]
  • 本文探讨了如何在C#应用程序中通过选择ComboBox项从MySQL数据库中检索数据值。具体介绍了在事件处理方法 `comboBox2_SelectedIndexChanged` 中可能出现的常见错误,并提供了详细的解决方案和优化建议,以确保数据能够正确且高效地从数据库中读取并显示在界面上。此外,还讨论了连接字符串的配置、SQL查询语句的编写以及异常处理的最佳实践,帮助开发者避免常见的陷阱并提高代码的健壮性。 ... [详细]
  • 本文提出了一种基于栈结构的高效四则运算表达式求值方法。该方法能够处理包含加、减、乘、除运算符以及十进制整数和小括号的算术表达式。通过定义和实现栈的基本操作,如入栈、出栈和判空等,算法能够准确地解析并计算输入的表达式,最终输出其计算结果。此方法不仅提高了计算效率,还增强了对复杂表达式的处理能力。 ... [详细]
  • Silverlight 实战指南:深入解析用户提交数据的验证与捕获机制
    本文深入探讨了Silverlight中用户提交数据的验证与捕获机制,详细分析了四种主要的验证方法:基本异常处理、DataAnnotation注解、IDataErrorInfo客户端同步验证以及自定义验证策略。通过实例解析,帮助开发者更好地理解和应用这些机制,提升应用程序的数据处理能力和用户体验。 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
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社区 版权所有