在这个TypeError消息中提到的“code object”是什么?

36

在尝试使用Python的exec语句时,我遇到了以下错误:

TypeError: exec: arg 1 must be a string, file, or code object

我不想传递一个字符串或文件,那么什么是代码对象,如何创建一个?


13
为什么要使用exec?传递字符串或文件有什么问题吗? - Josh Smeaton
它们在Python/C API参考手册的Code Objects部分中有描述。请参阅Python/C API参考手册(https://docs.python.org/3/c-api/index.html)。 - martineau
4个回答

37

创建代码对象的一种方法是使用内置函数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>

10
关于 OP 的问题,以及对这个回答的接受,令我困惑的是,如果他们要将一个字符串传递给 compile,那么大多数情况下他们可能会直接将其传递给 exec,并让 exec 在后台将其编译为代码对象。直接调用 compile 的唯一原因是如果应用程序有特定的需要来分离编译和执行步骤(例如同一个代码对象可能会被多次调用,或者即使不会立即运行,输入代码时也希望进行语法检查)。 - ncoghlan
编译命令输出中的行号表示什么? - YatShan
@YatShan:这不是编译命令的输出。这是自动调用repr(f.__code__)返回的字符串,Python交互式shell为了显示最后一个输入表达式(即f.__code__)的值而进行的操作。它说“第1行”,因为这是函数def的行号。 - martineau

30

这里有一篇优秀的博客文章,作者是丹·克罗斯塔(Dan Crosta),他解释了这个主题,包括如何手动创建代码对象,以及如何再次反汇编它们:

探索Python代码对象


3
好的链接。以备后用,如果该链接失效:该页面讨论通过compile创建代码对象,理解对象的内部结构,并略微查看底层字节码。 - Mike Williamson

18

关于Code objects的描述可以在这里找到:

Code objects表示已编译为字节码的Python可执行代码。和函数对象不同,代码对象不包含对函数全局变量(定义所在的模块)的显式引用;此外,函数对象中存储了默认参数值,而不是代码对象中存储(因为默认参数值代表运行时计算出的值)。与函数对象不同,代码对象是不可变的,且不包含对可变对象(直接或间接)的引用。


0
使用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

所以我们可以看到,这实际上解释了代码实际包含的内容,即Python字节码,并且在dis模块本身上有文档记录:https://docs.python.org/3.11/library/dis.html 在Python 3.11.4和Ubuntu 23.04上进行了测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接