繁体   English   中英

为什么在编组代码上调用exec的函数会给出关于全局变量的错误?

[英]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_NAMECALL_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM