Python基础必掌握的命名空间和作用域详解

学Python数据科学,玩游戏、学日语、搞编程一条龙。

整套学习自学教程中应用的数据都是《三國志》、《真·三國無雙》系列游戏中的内容。

比如创建一个变量 x 并赋值 data。

x = 'data'

赋值语句创建可用于引用对象的符号名称。那么在编程中程序一定是非常复杂的,会创建成百上千个这样变量名称,每个都指向一个特定的对象。那么 Python 如何跟踪所有这些名称以使它们不会相互干扰呢?

这里就要了解2个概念。

Python 命名空间 用于组织分配给程序中对象的符号名称的结构。在处理变量、函数、库和模块等,需要使用的变量的名称有可能已经作为另一个变量的名称或另一个函数或另一个方法的名称存在。在这种情况下需要了解所有这些名称是如何由 python 程序管理的。

Python 作用域 命名空间在可用时具有生命周期。这也称为范围。此外范围将取决于变量或对象所在的编码区域。另请注意外部函数的名称如何也成为全局变量的一部分。
在这里插入图片描述

Python 中的命名空间

命名空间 是当前定义的符号名称以及每个名称所引用的对象的信息的集合。可以将命名空间视为字典,其中键是对象名称,值是对象本身。每个键值 k,v 对都将一个名称映射到其对应的对象。

内置命名空间

内置命名空间包含所有 Python 内置对象的名称,并且在 Python 运行时始终可用。

扫描二维码关注公众号,回复: 14133335 查看本文章
dir(__builtins__)

['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', 'ZeroDivisionError', '_', '__build_class__', '__debug__',
 '__doc__', '__import__', '__loader__', '__name__', '__package__',
 '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', '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 在主程序体启动时创建全局命名空间,并且在解释器终止之前它一直存在。

严格来说全局命名空间不唯一,解释器还为程序随语句加载的任何模块创建一个全局命名空间。

本地和封闭的命名空间

每当函数执行时,解释器都会创建一个新的命名空间。该命名空间是函数的本地名称,并且在函数终止之前一直存在。

函数不会仅在主程序级别上彼此独立存在,还可以进行函数的嵌套。

def A():
    print('A() 函数开始')
    def B():
        print('B() 函数开始')
        print('B() 结束')
        return
    B()
    print('A() 结束')
    return

A()

A() 函数开始
B() 函数开始
B() 结束
A() 结束

变量范围

多个不同命名空间的存在意味着一个特定名称的多个不同实例可以在 Python 程序运行时同时存在。只要每个实例位于不同的命名空间中都会单独维护,不会相互干扰。

并且存在 本地、封闭、全局、内置 的使用顺序。

  • Local: 如果在函数内部引用 x,解释器首先在该函数本地的最内层范围内进行搜索。
  • Enclosing: 如果 x 不在本地范围内但出现在另一个函数内的函数中,则解释器在封闭函数的范围内搜索。
  • Global: Local、Enclosing搜索都没有结果,解释器接下来会在全局范围内查找。
  • Built-in: 如果 x 在其他任何地方都找不到,解释器尝试内置范围。

通过确定变量 x 的范围,解释器在运行时根据名称定义发生的位置以及代码中引用名称的位置来确定目标指向需要调用的变量 x 。
在这里插入图片描述

如果解释器在任何这些位置都找不到该名称,则 Python 会引发 NameError 异常。

# 示例 1:单独定义 x 变量位于全局范围内,函数定义之外
x = 'global'
def f():
   def g():
       print(x)
   g()
f()
global


# 示例 2:双重定义 x 变量位于函数体内和函数体外
x = 'global'
def f():
    x = 'enclosing'
    def g():
       print(x)
    g()
f()
enclosing


# 示例 3:三重定义 x 变量位于函数体内、函数体外和嵌套函数内
x = 'global'
def f():
    x = 'enclosing'
    def g():
        x = 'local'
        print(x)
    g()
f()
local


# 示例 4:没有定义,直接报错
def f():
    def g():
        print(x)
    g()
f()
Traceback (most recent call last):
  File "F:/PythonWorkProject/test.py", line 177, in <module>
    f()
  File "F:/PythonWorkProject/test.py", line 176, in f
    g()
  File "F:/PythonWorkProject/test.py", line 175, in g
    print(x)
NameError: name 'x' is not defined

Python 命名空间字典

Python 中全局和本地命名空间为字典,内置命名空间是一个模块。

访问全局和本地命名空间可以调用的内置函数 globals()locals()
在这里插入图片描述

globals()功能_

返回对当前全局命名空间字典的引用,用来访问全局命名空间中的对象。

type(globals())
<class 'dict'>

globals()
{
    
    '__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {
    
    }, '__builtins__': <module 'builtins' (built-in)>}


# 代码示例
x = '真·三國無雙'
globals()
{
    
    '__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {
    
    }, '__builtins__': <module 'builtins' (built-in)>,
'x': '真·三國無雙'}  # 新出现全局命名空间字典


# 访问调用
x
'真·三國無雙'
globals()['x']
'真·三國無雙'
x is globals()['x']
True


# 使用该函数在全局命名空间中创建和修改条目
globals()['y'] = 1
globals()
{
    
    '__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {
    
    }, '__builtins__': <module 'builtins' (built-in)>,
'x': '真', 'y': '·三國無雙'}
y
·三國無雙
globals()['y'] = 3.14
y
3.14

locals()功能_

是内置函数,功能类似于 globals() ,但访问本地命名空间中的对象。

def f(x, y):
    s = 'data'
    print(locals())

f(10, 0.5)
{
    
    's': 'data', 'y': 0.5, 'x': 10}

globals() 和 locals() 之间的细微差别

globals() 返回实际引用的对包含全局命名空间的字典。调用 globals() 保存返回值后定义其他变量,这些新变量将显示在保存的返回值指向的字典中。

g = globals()
g
{
    
    '__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {
    
    }, '__builtins__': <module 'builtins' (built-in)>,
'g': {
    
    ...}}

x = 'data'
y = 1
g
{
    
    '__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {
    
    }, '__builtins__': <module 'builtins' (built-in)>,
'g': {
    
    ...}, 'x': 'data', 'y': 1}

locals() 返回一个字典,是本地命名空间的当前副本,而不是引用。对本地命名空间的进一步添加不会影响之前的返回值 locals() 直到再次调用。不能使用返回值修改实际本地命名空间中的对象 locals()

def f():
    s = 'data'
    loc = locals()
    print(loc)
    x = 20
    print(loc)
    loc['s'] = 'cross'
    print(s)
    
f()
{
    
    's': 'data'}
{
    
    's': 'data'}
data

修改超出范围的变量

函数可以通过更改相应的参数来修改调用环境中的参数。

函数永远不能修改不可变参数。

x = 20
def f():
    x = 40
    print(x)
f()
40
x
20

可变参数不能全部重新定义,但可以修改。

my_list = ['曹操', '孫権', '劉備']
def f(my_list):
	# 仅在函数内部修改
    my_list[1] = '張遼'
    my_list=['典韋','満寵']

f(my_list)

print(my_list)
['曹操', '張遼', '劉備']

global宣言_

从函数体内部修改全局范围变量。

x = 20
def f():
    global x
    x = 40
    print(x)
    
    # 或者
    globals()['x'] = 40
    print(x)
f()
40
x
40

nonlocal宣言_

从嵌套函数体内部修改全局范围变量。

修改方式1,g()不能在全局范围内直接修改变量,也不能x在封闭函数的范围内修改。

def f():
    x = 2
    def g():
        x = 4
    g()
    print(x)
f()
2

修改方式2,由于x在封闭函数的范围内,而不是全局范围内,global关键字在这里不起作用。

def f():
    x = 2

   def g():
       global x
       x = 4
   g()
   print(x)
f()
2

修改方式3, global x语句不仅无法x在封闭范围内提供访问,而且还创建了一个x在全局范围内调用的对象。

def f():
    x = 2
    def g():
        global x
        x = 4
    g()
    print(x)
f()
2
x
4

global 和 nonlocal 关键字不建议频繁使用。

猜你喜欢

转载自blog.csdn.net/qq_20288327/article/details/124550124