执行上下文不同。在 REPL 中,我们逐行工作(Read、Eval、Print、L oop),它允许全局执行范围在每个步骤之间发生变化。但是执行模块的运行时是加载模块代码,然后在一个作用域内执行。
在 CPython 中,通过在全局命名空间中查找名称 __builtins__ 来找到与代码块执行相关联的内置命名空间;这应该绑定到字典或模块(在后一种情况下使用模块的字典)。在__main__ 模块中,__builtins__ 是内置模块builtins,否则__builtins__ 绑定到builtins 模块本身的字典。在您的问题的两种情况下,我们都在__main__ 模块中。
重要的是,CPython 仅在开始执行代码之前一次查找内置函数。在 REPL 中,每次执行新语句时都会发生这种情况。但是在执行 python 脚本时,脚本的全部内容是一个单元。这就是为什么删除脚本中间的内置函数没有效果的原因。
为了在 REPL 中更准确地复制该上下文,您不会逐行输入模块的代码,而是使用复合语句:
>>> if 1:
... del __builtins__
... print(123)
...
123
>>> print(123)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'print' is not defined
当然,您现在可能想知道如何从脚本中删除内置函数。答案应该很明显:你不能通过重新绑定名称来做到这一点,但你可以通过突变来做到:
# foo2.py
__builtins__.__dict__.clear()
print(int) # <- NameError: name 'print' is not defined
最后一点,__builtins__ 名称被绑定的事实是 CPython 的 implementation detail,并且已明确记录:
用户不应触碰__builtins__;这严格来说是一个实现细节。
不要依赖__builtins__ 做任何严重的事情,如果您需要访问该范围,正确的方法是访问import builtins 并从那里开始。