在昨天的一个问题中,在评论中,我发现在python __code__
函数中,atrribute是一个可变的。因此我可以编写如下代码
def foo():
print "Hello"
def foo2():
print "Hello 2"
foo()
foo.__code__ = foo2.__code__
foo()
输出
Hello
Hello 2
我尝试使用Google搜索,但要么是因为没有信息(我非常怀疑这一点),要么关键字(__code__
)不容易搜索,我找不到一个用例。
它似乎不是“因为Python中的大多数东西都是可变的”也是一个合理的答案,因为函数的其他属性 - __closure__
和__globals__
- 是显式只读的(来自{ {3}}):
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__
可写,而其他属性是只读的?
答案 0 :(得分:4)
事实是,Python 中的大部分内容都是可变的。所以真正的问题是,为什么__closure__
和__globals__
不是?
答案最初看似简单。这两个东西都是函数可能需要的变量的容器。代码对象本身不带有它的封闭和全局变量;它只知道如何从函数中获取它们。在调用函数时,它会从这两个属性中获取实际值。
但是范围本身是可变的,所以这个答案并不令人满意。我们需要解释为什么特别修改这些东西会破坏它们。
对于__closure__
,我们可以看一下它的结构。它不是映射,而是一个单元格元组。它不知道已关闭变量的名称。当代码对象查找一个封闭的变量时,它需要知道它在元组中的位置;它们与co_freevars
一对一匹配,这也是只读的。如果元组的大小错误或者根本没有元组,那么如果基础C代码没有预料到这种情况,这种机制可能会发生剧烈破坏(读:segfaults)。强制C代码检查元组的类型和大小是不必要的繁忙工作,可以通过将属性设置为只读来消除。如果您尝试将__code__
替换为使用不同数量的自由变量you get an error的内容,那么大小始终是正确的。
对于__globals__
,解释不太明显,但我推测。范围查找机制希望始终可以访问全局命名空间。实际上,如果编译器可以证明其他命名空间没有具有特定名称的变量,则字节码可以hard-coded直接进入全局命名空间。如果全局命名空间突然None
或其他一些非映射对象,那么C代码可能再一次暴力行为不端。再次,使代码执行不必要的类型检查将浪费CPU周期。
另一种可能性是(通常声明的)函数borrow a reference到模块的全局命名空间,并且使属性可写将导致引用计数混乱。我可以想象这个设计,但我并不确定这是一个好主意,因为可以使用寿命可能比拥有模块短的对象明确构造函数,这些对象需要特殊 - 套管。