代码之家  ›  专栏  ›  技术社区  ›  Aran-Fey Swapnil

为什么删除名为uu builtins\uuuu的全局变量只会阻止REPL访问内置变量?

  •  2
  • Aran-Fey Swapnil  · 技术社区  · 6 年前

    我有一个包含以下内容的python脚本:

    # foo.py
    
    __builtins__ = 3
    del __builtins__
    
    print(int)  # <- this still works
    

    -i 标志阻止 无法访问内置的REPL:

    aran-fey@starlight ~> python3 -i foo.py 
    <class 'int'>
    >>> print(int)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'print' is not defined
    

    为什么脚本可以访问内置,而REPL不能?

    2 回复  |  直到 6 年前
        1
  •  3
  •   user2357112    6 年前

    CPython没有抬头 __builtins__ 每次它需要做一个内置的变量查找。每个帧对象都有一个 f_builtins

    f\内置 在创建帧对象时设置。如果一个新的框架 ( f_back 不同的全局变量dict __内置__ 设置 f\内置 . (如果新框架与其父框架共享一个全局dict,那么它将继承其父框架的全局dict f\内置 __内置__ 包含在内置变量查找中。您可以在中看到处理此问题的代码 _PyFrame_New_NoTrack

    删除时 __内置__ 在脚本中,这不会影响 . 在脚本的堆栈框架中执行的其余代码仍然可以看到内置代码。一旦脚本完成 -i 使您进入交互模式,每个交互命令都会获得一个新的堆栈帧(没有父级),并且 重复查找。这是删除 __内置__ 最后一件事。

        2
  •  3
  •   Aran-Fey Swapnil    6 年前

    执行上下文不同。在REPL内,我们正在一行一行地工作( 电子 oop),允许全局执行范围在每个步骤之间发生变化。但是执行模块的运行时是加载模块代码,然后在一个范围内执行它。

    在CPython中,通过查找名称可以找到与代码块的执行相关联的内置名称空间 __builtins__ __main__ 模块, __内置__ 是内置模块 builtins ,否则 __内置__ 内置 模块本身。在你的两个问题中,我们都处于 模块。

    重要的是,CPython只查找内置的

    要在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
    

    __内置__ implementation detail 关于CPython的,并且有明确的记录:

    __内置__ ;这是一个严格的实施细节。

    不要依赖于 __内置__ 对于任何严重的问题,如果您需要访问该范围,正确的方法是 import builtins 从那里开始。