8

在玩弄marshal模块compile()和. 我遇到了一些令人困惑的行为。考虑 execsimple.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())上使用时,我看到的唯一东西是LOAD_NAMEand CALL_FUNCTIONfor main(),但是当我这样做import

import dis, sys

import simple
dis.dis(sys.modules["simple"])

它按预期给了我整个反汇编。

我什至查看了 python 用于编译的一些代码,虽然我认为import使用某种查找表进行定义,但我不确定compile()导致这种行为的区别是什么。

4

2 回答 2

0

该脚本成功运行了您的 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)
于 2012-08-29T19:54:03.930 回答
0

为什么它说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() 在函数内部调用时工作方式不同以及如何避免它的答案。为了完整起见,以下是我建议您在这种情况下解决它的方法。

由于您可以更改execed 代码(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编辑simple.py而不是编辑exec它的内容。不仅会foo出现在 的输出中dir(),而且当您使用import.

import simple
simple.main()

如果这让你感到惊讶,那是因为当你import做某事时,Python 将它视为一个模块。在一个模块中,在顶层声明的东西会自动变成全局的。

关于令人困惑的输出dis.dis,我无法重现该行为,因此我无法研究它并提供解释。

于 2019-04-28T17:50:14.473 回答