gpt4 book ai didi

python - 为什么函数(Python)的 __code__ 是可变的

转载 作者:太空狗 更新时间:2023-10-29 16:58:16 25 4
gpt4 key购买 nike

在昨天的前一个问题中,在评论中,我了解到在 python __code__ 中函数的属性是可变的。因此我可以编写如下代码

def foo():
print "Hello"

def foo2():
print "Hello 2"

foo()
foo.__code__ = foo2.__code__
foo()

输出

Hello
Hello 2

我试过谷歌搜索,但要么是因为没有信息(我对此非常怀疑),要么是关键字 (__code__) 不容易搜索到,我找不到这个的用例。

“因为 Python 中的大多数东西都是可变的”似乎也不是一个合理的答案,因为函数的其他属性——__closure____globals__——是明确的只读(来自 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__ 是可写的,而其他属性是只读的?

最佳答案

事实上,Python 中的大部分内容都是可变的。所以真正的问题是,为什么 __closure____globals__ 不是

答案最初看起来很简单。这两件事都是函数可能需要的变量的容器。代码对象本身并不携带它的封闭变量和全局变量;它只知道如何从函数中获取它们。它在调用函数时从这两个属性中获取实际值。

但是范围本身是可变的,所以这个答案并不令人满意。我们需要解释为什么特别修改这些东西会破坏东西。

对于__closure__,我们可以看看它的结构。它不是映射,而是单元格的元组。它不知道封闭变量的名称。当代码对象查找一个封闭变量时,它需要知道它在元组中的位置;它们与也是只读的 co_freevars 一对一匹配。如果元组的大小错误或者根本不是元组,那么如果底层 C 代码没有预料到这种情况,这种机制可能会崩溃(读作:段错误)。强制 C 代码检查元组的类型和大小是不必要的繁重工作,可以通过将属性设为只读来消除这一点。如果您尝试将 __code__ 替换为采用不同数量的自由变量的东西,you get an error , 所以尺寸总是合适的。

对于 __globals__,解释不是很明显,但我会推测。范围查找机制期望在任何时候都可以访问全局命名空间。实际上,字节码可能是 hard-coded直接进入全局命名空间,如果编译器可以证明没有其他命名空间将具有具有特定名称的变量。如果全局命名空间突然变为 None 或其他一些非映射对象,C 代码可能会再次出现严重的错误行为。同样,让代码执行不必要的类型检查会浪费 CPU 周期。

另一种可能性是(通常声明的)函数 borrow a reference到模块的全局命名空间,并使属性可写会导致引用计数困惑。我可以想象这种设计,但我不太确定这是个好主意,因为可以使用生命周期可能比拥有模块的生命周期更短的对象显式构造函数,并且这些需要特殊情况。

关于python - 为什么函数(Python)的 __code__ 是可变的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31599376/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com