L8C*_*d3r 2 python eval local-variables python-3.x
我有以下几点:
x = [1,2,3,4,5]
def foo(lbd:str, value):
ret_val = eval(lbd, globals(), locals())
print(ret_val)
Run Code Online (Sandbox Code Playgroud)
在此调用中使用 'value' 变量成功:
>>> foo("[i for i in value]",x)
[1, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)
但是这个失败了:
>>> foo(r"any([x in value for x in {'',0,None,'0'}])", x)
Traceback (most recent call last):
File "<pyshell#171>", line 1, in <module>
foo(r"any([x in value for x in {'',0,None,'0'}])", x)
File "<pyshell#165>", line 2, in foo
ret_val = eval(lbd, globals(), locals())
File "<string>", line 1, in <module>
File "<string>", line 1, in <listcomp>
NameError: name 'value' is not defined
Run Code Online (Sandbox Code Playgroud)
我能够解决这个问题,但很想知道这里发生了什么。
>>> foo(r"(lambda V=value: any([x in V for x in {'',0,None,'0'}]) )()", x)
False
Run Code Online (Sandbox Code Playgroud)
这是一个超级微妙的点。因此,如果您阅读 的文档eval,它没有提到您为全局变量和本地变量提供参数的情况,但我相当确定它的工作原理与forexec相同:
如果
exec获取两个单独的对象作为全局对象和局部对象,则代码将被执行,就像它嵌入在类定义中一样。
在类定义中,函数无法访问其封闭范围。所以这与错误完全相同:
>>> class Foo:
... value = [1,2,3]
... print([x in value for x in [2,4,6]])
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in Foo
File "<stdin>", line 3, in <listcomp>
NameError: name 'value' is not defined
Run Code Online (Sandbox Code Playgroud)
因为列表推导式通过在幕后创建一个函数对象来工作。这也是您需要self.some_method访问类中定义的其他方法的名称的原因。在此处接受的优秀答案中有更多关于上述内容的信息。
所以它是一样的:
>>> def foo():
... x = 3
... return eval('(lambda: x + 1)()', globals(), locals())
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
File "<string>", line 1, in <module>
File "<string>", line 1, in <lambda>
NameError: name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)
但是,这很好用:
>>> def foo():
... x = 3
... return eval('x + 1', globals(), locals())
...
>>> foo()
4
Run Code Online (Sandbox Code Playgroud)
因为不涉及(非)封闭的函数范围。
最后,以下工作的原因:
>>> def foo():
... values = [1,2,3]
... return eval('[x+2 for x in values]', globals(), locals())
...
>>> foo()
[3, 4, 5]
Run Code Online (Sandbox Code Playgroud)
是因为推导式最左边的for 子句中的迭代不是在推导式的函数范围内进行评估,而是在推导式发生的范围内进行评估(它实际上是作为参数传递的)。您可以在列表推导式的反汇编中看到这一点:
>>> import dis
>>> dis.dis('[x+2 for x in values]')
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x7fe28baee3a0, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_NAME 0 (values)
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7fe28baee3a0, file "<dis>", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (x)
8 LOAD_FAST 1 (x)
10 LOAD_CONST 0 (2)
12 BINARY_ADD
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
注意,values被评估,iter被调用,结果被传递给函数:
6 LOAD_NAME 0 (values)
8 GET_ITER
10 CALL_FUNCTION 1
Run Code Online (Sandbox Code Playgroud)
“函数”基本上只是一个带有 append 的循环,请参阅:Disassembly of <code object <listcomp> at 0x7fe28baee3a0, file "<dis>", line 1>了解列表推导式如何工作。
| 归档时间: |
|
| 查看次数: |
70 次 |
| 最近记录: |