关于python:将exit()作为全局函数调用会导致KeyError

Calling exit() as a global function causes KeyError

我正在为我现在正在编写的程序编写模块管理器,我想将模块名存储在字典中,然后引用它们并从globals()调用它们。

1
2
module_number = 5
module_names  = ["","quiz","scores","gender","help","exit"]

那我就可以打电话了

1
globals()[module_names[module_number]]()

它将调用exit()并关闭脚本,相反,我得到结果错误:

Traceback (most recent call last): File"a2.py", line 103, in start() File"a2.py", line 44, in start menu() File"a2.py", line 36, in menu call_module(choice) File"a2.py", line 50, in call_module globals()converter[int(module_number)]

KeyError: 'exit'


exit不在globals()中,因为它不是全球性的,而是内置的。

在Python中,"全局"名称空间是每个模块的,而不是系统范围的。有一个特殊的"内置"模块,它可以容纳真正系统范围内的内容,比如正常的内置功能和一些特殊的内容,比如exit

您可以使用import builtins访问此模块。

解释器访问这个模块的方式有点奇怪。全局查找的工作原理大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def get_global(global_namespace, name):
    try:
        return global_namespace[name]
    except KeyError:
        pass
    try:
        builtins = global_namespace['__builtins__']
    except KeyError:
        raise NameError(name)
    if isinstance(builtins, types.ModuleType):
        builtins = builtins.__dict__
    try:
        return builtins[name]
    except KeyError:
        raise NameError(name)

在像exec这样的地方有特殊的代码,以及用于构建函数对象的内部代码,这可以确保如果您重写了正常的globals字典,__builtins__就会被复制过来(除非您明确地告诉它不要这样做)。当导入系统从模块源(或编译的.pyc中)构建模块对象时,它调用exec,因此每个模块的全局都以右边的__builtins__结束。

builtins模块中的大多数内容都存在,因为它们是编译成的(正如您从名称中所期望的那样);对于cpython,您可以在Python/bltinmodule.c中看到源代码。

但请注意,exit不在那里。实际上,它是由site模块注入到builtins模块中的,该模块作为正常启动序列的一部分导入(除非禁用)。您可以在Lib/site.pyLib/_sitebuiltins.py中看到执行此操作的代码。exit常数表示它是这样注入的。

因此,当您在代码中或在交互提示下键入exit时,可以在globals()['__builtins__']['exit']globals()['__builtins__'].__dict__['exit']中找到它。

但如果您想手动访问它,最好是执行import builtins并将其作为builtins.exit访问。

尽管如此,您还是很少想访问builtins.exit;如果您想以编程方式退出,请调用sys.exit,这是一个正常的函数。builtins.exit是一个特殊的Quitter对象,专门用于交互使用。(它有一个repr,如果您忘记了括号,它会给出一个有用的消息,并提供一些额外的代码,使它能很好地处理空闲状态。)

事实上,exit常量上的文档明确表示它是:

… useful for the interactive interpreter shell and should not be used in programs.