[英]Why does my function that calls exec on marshalled code give an error about globals?
在使用compile()
, marshal模块和exec
。 我遇到了一些令人困惑的行为。 考虑simple.py
def foo():
print "Inside foo()..."
def main():
print "This is a simple script that should count to 3."
for i in range(1, 4):
print "This is iteration number", i
foo()
if __name__ == "__main__":
main()
当我像这样使用exec
运行此脚本时
with open('simple.py', 'r') as f:
code = f.read()
exec code
它给出了预期的输出。
This is a simple script that should count to 3.
This is iteration number 1
This is iteration number 2
This is iteration number 3
Inside foo()...
但是,当我像这样介绍compile()
, marshal.dump()
和marshal.load()
import marshal
def runme(file):
with open(file, "r") as f:
code = marshal.load(f)
exec code
with open("simple.py", "r") as f:
contents = f.read()
code = compile(contents, "simple.py", "exec")
with open("marshalled", "w") as f:
marshal.dump(code, f)
runme("marshalled")
它输出预期输出的开始,然后输出错误
This is a simple script that should count to 3.
This is iteration number 1
This is iteration number 2
This is iteration number 3
Traceback (most recent call last):
File "./exec_within_function.py", line 17, in <module>
runme("marshalled")
File "./exec_within_function.py", line 8, in runme
exec code
File "simple.py", line 15, in <module>
main()
File "simple.py", line 12, in main
foo()
NameError: global name 'foo' is not defined
为什么说没有定义foo
?
为了理解,我尝试使用dir()
这样
import simple # imports simple.py
dir(simple)
如预期的那样,它表明已定义foo
。
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'foo', 'main']
我还注意到,当我在反序列化的代码对象上使用dis.dis()
(通过marshal.load()
读取)时,我唯一看到的是main()
的LOAD_NAME
和CALL_FUNCTION
,但是当我使用像这样import
import dis, sys
import simple
dis.dis(sys.modules["simple"])
它给了我整个拆卸的期望。
我什至已经看过python用于编译的一些代码,尽管我认为import
使用某种形式的查找表进行定义,但我不确定与compile()
有什么区别会导致这种行为。
该脚本成功地练习了您的simple.py代码3次。 这有什么澄清吗? 还是我误会了你的问题?
# from original example
with open('simple.py', 'r') as f:
code = f.read()
exec(code)
# compile and run again
a = compile(code, "simple_compiled_this_file_not_created", "exec")
exec(a)
# marshal and unmarshal
import marshal
f = open("./marshalfoo.bin", "wb")
marshal.dump(a,f)
f.close()
b = marshal.load(open("./marshalfoo.bin", "rb"))
exec(b)
为什么说没有定义
foo
?
这个小得多的示例给出了相同的错误
with open("simple.py", "r") as f:
code = f.read()
def wrap_exec(code):
exec code
wrap_exec(code)
但这不是。
with open("simple.py", "r") as f:
code = f.read()
exec code
如果您还没有猜到,那么从函数内部调用exec
时就会出现问题。
要找到最佳的解释和解决方案,请查看exec()为什么在函数内部被调用时工作原理不同的答案, 以及如何避免使用它 。 为了完整起见,以下是我建议您在这种情况下解决该问题的方法。
由于您有权更改exec
代码(在此示例中为simple.py
),因此可以通过添加global
声明轻松解决此问题。
global foo # added
def foo():
print "Inside foo()..."
def main():
print "This is a simple script that should count to 3."
for i in range(1, 4):
print "This is iteration number", i
foo()
if __name__ == "__main__":
main()
关于dir(simple)
为何仍显示foo
,实际上是因为您import
ed simple.py
而不是exec
其内容。 foo
不仅会出现在dir()
的输出中,而且当您使用import
时,该程序也会起作用。
import simple
simple.main()
如果这让您感到惊讶,那是因为在import
,Python将其视为模块。 在模块内,在顶层声明的内容会自动变为全局。
关于dis.dis
令人困惑的输出,我无法重现该行为,因此无法对其进行研究并提供解释。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.