在昨天的前一个问题中,在评论中,我了解到在python __code__
中,函数的属性是可变的。因此,我可以编写如下代码
def foo():
print "Hello"
def foo2():
print "Hello 2"
foo()
foo.__code__ = foo2.__code__
foo()
输出
Hello
Hello 2
我尝试了谷歌搜索,但要么是因为没有信息(我非常怀疑这一点),要么是因为关键字(__code__
)不容易搜索,我找不到这方面的用例。
“因为Python中的大多数东西都是可变的”似乎也不是一个合理的答案,因为函数的其他属性- __closure__
和__globals__
- are显式只读(来自Objects/funcobject.c):
static PyMemberDef func_memberlist[] = {
{"__closure__", T_OBJECT, OFF(func_closure),
RESTRICTED|READONLY},
{"__doc__", T_OBJECT, OFF(func_doc), PY_WRITE_RESTRICTED},
{"__globals__", T_OBJECT, OFF(func_globals),
RESTRICTED|READONLY},
{"__module__", T_OBJECT, OFF(func_module), PY_WRITE_RESTRICTED},
{NULL} /* Sentinel */
};
为什么__code__
是可写的,而其他属性是只读的?
发布于 2015-07-24 08:28:48
事实是,Python中的大多数东西都是可变的。所以真正的问题是,为什么__closure__
和__globals__
不是
答案最初看起来很简单。这两件事都是函数可能需要的变量的容器。代码对象本身并不携带它的封闭变量和全局变量;它只知道如何从函数中获取它们。在调用函数时,它会从这两个属性中获取实际值。
但是作用域本身是可变的,所以这个答案并不令人满意。我们需要解释为什么修改这些东西会破坏东西。
对于__closure__
,我们可以看看它的结构。它不是一个映射,而是一个元组。它不知道封闭变量的名称。当代码对象查找闭合变量时,它需要知道它在元组中的位置;它们与同样是只读的co_freevars
一对一地匹配。如果元组的大小错误或者根本不是元组,这种机制就会崩溃,如果底层C代码不希望出现这种情况,这种机制很可能会崩溃(读作: segfaults)。强制C代码检查元组的类型和大小是不必要的繁忙工作,这可以通过将属性设为只读来消除。如果您尝试用具有不同数量的自由变量的东西来替换__code__
,you get an error,因此大小总是正确的。
对于__globals__
,解释不是很明显,但我会猜测。作用域查找机制期望始终能够访问全局命名空间。实际上,如果编译器可以证明没有其他名称空间具有具有特定名称的变量,则字节码可以是hard-coded,以直接转到全局名称空间。如果全局名称空间突然变成了None
或其他非映射对象,C代码可能会再一次出现严重的错误行为。同样,让代码执行不必要的类型检查将是对CPU周期的浪费。
另一种可能性是(通常声明的)函数可写到模块的全局名称空间,并且将属性设为可写将导致引用计数混乱。我可以想象这种设计,但我不确定这是不是一个好主意,因为函数可以用生命周期可能比拥有模块的生命周期短的对象显式构造,并且这些对象需要特殊大小写。
https://stackoverflow.com/questions/31599376
复制相似问题