在尝试使用Python的exec
语句时,我遇到了以下错误:
TypeError: exec: arg 1 must be a string, file, or code object
我不想传递一个字符串或文件,那么什么是代码对象,如何创建一个?
在尝试使用Python的exec
语句时,我遇到了以下错误:
TypeError: exec: arg 1 must be a string, file, or code object
我不想传递一个字符串或文件,那么什么是代码对象,如何创建一个?
创建代码对象的一种方法是使用内置函数compile
:
>>> compile('sum([1, 2, 3])', '', 'single')
<code object <module> at 0x19ad730, file "", line 1>
>>> exec compile('sum([1, 2, 3])', '', 'single')
6
>>> compile('print "Hello world"', '', 'exec')
<code object <module> at 0x19add30, file "", line 1>
>>> exec compile('print "Hello world"', '', 'exec')
Hello world
此外,函数具有函数属性__code__
(在旧版本中也称为func_code
),通过它可以获取函数的代码对象:
>>> def f(s): print s
...
>>> f.__code__
<code object f at 0x19aa1b0, file "<stdin>", line 1>
compile
,那么大多数情况下他们可能会直接将其传递给 exec
,并让 exec
在后台将其编译为代码对象。直接调用 compile
的唯一原因是如果应用程序有特定的需要来分离编译和执行步骤(例如同一个代码对象可能会被多次调用,或者即使不会立即运行,输入代码时也希望进行语法检查)。 - ncoghlanrepr(f.__code__)
返回的字符串,Python交互式shell为了显示最后一个输入表达式(即f.__code__
)的值而进行的操作。它说“第1行”,因为这是函数def
的行号。 - martineau这里有一篇优秀的博客文章,作者是丹·克罗斯塔(Dan Crosta),他解释了这个主题,包括如何手动创建代码对象,以及如何再次反汇编它们:
compile
创建代码对象,理解对象的内部结构,并略微查看底层字节码。 - Mike Williamson关于Code objects的描述可以在这里找到:
Code objects表示已编译为字节码的Python可执行代码。和函数对象不同,代码对象不包含对函数全局变量(定义所在的模块)的显式引用;此外,函数对象中存储了默认参数值,而不是代码对象中存储(因为默认参数值代表运行时计算出的值)。与函数对象不同,代码对象是不可变的,且不包含对可变对象(直接或间接)的引用。
dis.dis
来解析代码,看看它包含了什么内容。code
对象的一个基本方法是将其解析为在Daniel的回答链接中提到的。import ast
import dis
myast = ast.parse('''def inc(i):
return i + 1
print(inc(2))
''')
# print(ast.dump(myast, indent=2))
print(ast.unparse(myast))
code = compile(myast, 'mymodule', 'exec')
exec(code)
dis.dis(code)
输出:
def inc(i):
return i + 1
print(inc(2))
3
0 0 RESUME 0
1 2 LOAD_CONST 0 (<code object inc at 0x7f85fbfae800, file "mymodule", line 1>)
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (inc)
4 8 PUSH_NULL
10 LOAD_NAME 1 (print)
12 PUSH_NULL
14 LOAD_NAME 0 (inc)
16 LOAD_CONST 1 (2)
18 PRECALL 1
22 CALL 1
32 PRECALL 1
36 CALL 1
46 POP_TOP
48 LOAD_CONST 2 (None)
50 RETURN_VALUE
Disassembly of <code object inc at 0x7f85fbfae800, file "mymodule", line 1>:
1 0 RESUME 0
2 2 LOAD_FAST 0 (i)
4 LOAD_CONST 1 (1)
6 BINARY_OP 0 (+)
10 RETURN_VALUE
dis
模块本身上有文档记录:https://docs.python.org/3.11/library/dis.html
在Python 3.11.4和Ubuntu 23.04上进行了测试。