Python 中的命名空间

2024年8月29日 | 阅读 7 分钟

在本教程中,我们将学习 Python 中的命名空间,用于组织 Python 程序中分配给对象的符号名称的结构,命名空间的重要性以及如何在 Python 程序中使用它们。让我们简要介绍一下命名空间。

什么是命名空间?

在 Python 中,通过命名空间为每个对象赋予唯一名称。变量和方法是 Python 中对象的示例。换句话说,它是一个已知符号名称的集合以及每个名称所指对象的详细信息。名称可以被认为是字典中的一个键,而对象是命名空间中的值。我们应该通过一个真实的模型来理解它——命名空间类似于姓氏。如果班级中有多个名为“Peter”的人,那么找到一个名为“Peter”的人可能会很困难;但是,当我们明确要求“Peter Warner”或“Peter Cummins”时,情况就不同了。在班级中,同一个名字和姓氏的学生可能并不常见。

命名空间使 Python 解释器能够更好地理解代码中的确切方法或变量。因此,它的名称包含额外的信息,包括 Space(与作用域相关)和 Name,表示一个唯一的标识符。

在 Python 中,有四种类型的命名空间,如下所示。

  • 建造时间
  • 全球
  • 嵌套
  • 局部

由于这些命名空间的生命周期各不相同,Python 解释器会根据需要创建命名空间,并在不再需要时删除它们。

让我们了解 Python 中各种类型的命名空间。

内置命名空间

顾名思义,它包含了 Python 中所有内置对象的预定义名称,这些对象已在 Python 中可用。让我们使用以下命令列出这些名称。

打开 Python 终端并键入以下命令。

命令 -

输出

['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 
'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 
'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

内置命名空间在 Python 启动时由 Python 解释器创建。当 Python 解释器终止时,这些命名空间也会被终止。

全局命名空间

全局命名空间包含主程序任何级别的 Python 中的所有名称。它在主程序体执行时创建,并在解释器终止之前一直存在。

Python 解释器为我们使用 `import` 语句加载的任何模块创建一个全局命名空间。如需更多信息,请访问我们的 Python 模块。

局部和嵌套命名空间

局部命名空间由函数使用;当函数运行时,Python 解释器会创建一个新的命名空间。局部命名空间在函数运行结束后继续存在。这种能力也可以包含另一个函数。如下所示,我们可以在一个函数内定义另一个函数。

示例 -

在上面的模型中,函数 `g()` 定义在 `f()` 的范围内。我们在 `f()` 内部调用了 `g()` 函数,然后又调用了 `f()` 函数。让我们看看上面的函数是如何工作的。

  • 当我们调用 `f()` 时,Python 会为 `f()` 创建一个新的命名空间。
  • 同样,`f()` 调用 `g()`,`g()` 会获得自己的独立命名空间。
  • `g()` 的局部命名空间是为嵌套命名空间 `f()` 创建的。

当函数终止时,这些命名空间中的每一个都会终止。

对象/变量的作用域

“作用域”一词指定了特定 Python 对象可以被访问的代码区域。每个对象和变量在程序中都有一个作用域,我们可以从中访问该变量。例如,函数变量只能在函数内部访问。让我们检查以下说明。

示例 -

输出

Inside scope_func
Inside inner function, value of var: 20
Traceback (most recent call last):
  File "d:/Python Project/listproblems.py", line 343, in 
    scope_func()
  File "d:/Python Project/listproblems.py", line 342, in scope_func
    print("Try printing var from outer function: ",var)
NameError: name 'var' is not defined

Python 命名空间字典

在之前的教程中,我们讨论了命名空间如何类似于字典,其中键代表对象名称,值代表实际对象。Python 使用全局和局部命名空间作为字典。Python 的 `globals()` 和 `locals()` 方法提供了访问全局和局部命名空间字典的功能。

globals() 方法

`globals()` 方法返回对当前全局命名空间字典的引用。我们可以使用它来访问全局命名空间中的对象。让我们看下面的例子。

示例 -

正如我们所见,`globals()` 方法中包含许多内置条目。根据您的操作系统和 Python 版本,这些条目可能会有所不同。现在让我们定义一个全局变量并观察差异。

在赋值 `a = 20` 之后,一个新的全局变量被分配给全局命名空间字典。我们可以像访问字典一样访问值。让我们看下面的例子。

我们可以使用 `globals()` 函数修改字典值。

现在 `a` 的新值将出现在全局字典中。

locals() 函数

Python 还提供了与 `globals()` 类似的 `locals()` 方法,但它访问的是局部命名空间中的对象。让我们看下面的例子。

示例 -

当我们调用 `func(10, 20)` 时,`locals()` 返回代表函数局部命名空间的字典。在函数作用域中,我们定义了局部变量 `str1`;局部命名空间包含了函数参数,因为它们是 `func()` 的局部变量。

尽管如此,当我们调用 `locals()` 函数时,它的行为与 `globals()` 函数相同。`globals()` 函数和 `locals()` 函数略有不同。`globals()` 函数不仅定义了额外的变量,还存储了返回值。字典将包含新变量及其值。请看下面的例子。

示例 -

在这里,`glob_var` 是对全局命名空间字典的引用。新的赋值语句 `x` 和 `y` 出现在 `glob_var` 字典中。

更改作用域外的变量

在调用环境中,函数可以通过传递不同的值来更改参数,但有时它无法更改该值。

不可变参数不能被函数修改。

可变参数可以在原地修改,但不能完全重新定义。

让我们理解以下场景。

示例 -

输出

40
20

我们定义了一个全局变量 `x = 20`,并在函数中定义了同名变量。当 `func()` 执行时,它会创建一个指向值为 40 的整数对象的新局部变量引用。在 `func()` 内部,赋值语句不会影响全局对象。

然而,函数可以修改其局部作用域之外的可变类型对象。让我们理解下面的例子。

示例 -

`my_list` 是一个列表,它是可变类型。即使 `my_list` 在局部作用域之外,`func()` 也可以修改 `my_list` 内部。但是,如果我们尝试重新分配 `my_list`,它将创建一个新的局部对象,而不会修改全局 `my_list`。让我们看下面的例子。

示例 -

输出

['A', 'B', 'C', 'D', 'E']

结论

我们已经了解了命名空间,如何使用它以及变量的作用域。通过一个简短的 Python 程序可以创建许多不同的对象。在一个复杂的 Python 程序中,这个数量可能超过一千。Python 命名空间使解释器能够更容易地记住这些对象的名称。