Python编译器生成的代码对象包含指令中使用的常量元组(名为co_consts
),还包含一个包含名称的元组(名为co_names
)。
为什么要有两个不同的列表?仅仅使用co_consts
作为名称也不会更简单吗?
答案 0 :(得分:6)
考虑以下功能。
def f(x):
x += n
return x * 4
此处x
是本地名称,其值可以更改。 4
是一个常量。它的价值永远不会改变。但是,它仍然是一个对象,最好缓存它们,而不是每次需要时创建一个新对象。最后,n
是全球参考。字符串"n"
由函数存储,以便它可以用作从函数的全局上下文中检索n
的键。
>>> f.__code__.co_nlocals # just 1 (for x)
1
>>> f.__code__.co_consts
(None, 4)
>>> f.__code__.co_names
('n',)
>>> "n" in f.__globals__ and globals() is f.__globals__
True
保持名称和内容分离的原因是为了内省。合并元组的唯一真正原因是内存效率,尽管这只会获得一个对象和每个函数一个指针。请考虑以下功能。
def g():
return "s" * n
如果包含consts的元组与包含名称的元组合并,那么你(不是VM)将无法分辨哪些值用于访问全局变量以及哪些是函数的常量。
答案 1 :(得分:1)
我知道这个答案就像是过时了11个月,但是从我的修修补补来看似乎发生了以下情况
要访问字节码中的co_names,可以使用LOAD_GLOBAL(co name index),这会将对所需co_names的引用推送到堆栈上,例如其间接
要访问字节码中的co_consts,可以使用LOAD_CONST(co consts index),这会将存储在所需co_consts中的实际值推送到堆栈上,例如直接
我不确定它在python级别有任何直接影响,但在字节码级别它有很大差异