python 名称空间与作用域(笔记整理)

发布时间 2023-05-26 11:53:23作者: 王献运

一、 名称空间

  • 什么是名称空间:名称空间就是存放变量名和变量值绑定关系的地方就是内存地址
  • 在程序执行期间最多会存在三种名称空间:
    • 内置名称空间:是 Python 解释器默认预定义大量内置函数和内置异常的名称空间,就是存放解释器自带函数方法的名称空间
      • 可以通过 dir(builtins) 来查看内置名称空间中所有的内置函数和对象:
print(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', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', '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', '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']
  • 全局名称空间:就是在 “Py文件”内的都属于全局名称空间,就是用来存放我们自己定义的一些变量或者对象和模块之类的,就是存放我们自己定义的数据的内存空间
1.我们可以在一个 test.py 的模块文件中定义全局变量 a 和函数 add()
定义全局变量 a
a = 10

# 定义函数 add()
def add(x, y):
    return x + y

2.然后在另一个 Python 文件中导入该模块,并使用 dir(test) 查看该模块中的全局名称空间中所有的变量、函数和类等对象信息
import test
# 查看全局名称空间中的对象
print(dir(test))

  • 局部名称空间:用来存放在函数中定义的变量和函数就是在函数中定义的数据的名称空间,在函数被调用时由解释器创建
    • 局部名称空间只在该函数执行期间存在,在函数执行结束后将被销毁。
1.我们可以在一个 test.py 的模块文件中定义一个名为 hello() 的函数,该函数包含一个名为 name 的形参和一个名为 message 的局部变量
# 定义函数 hello()
def hello():
    # 定义局部变量 message
    message = f"Hello {name}!"
    print(message)
    
2.然后在另一个 Python 文件中调用该函数时,解释器会为该函数创建一个局部名称空间,并将传入的实参 Tom 存储在 name 形参对应的位置上:
import test

# 调用函数 hello()
res = test.hello()
print(dir(res))
  • 名称空间的加载顺序是:内置名称空间 全局名称空间 局部名称空间
  • 名称空间的查找顺序是:局部名称空间 全局名称空间** →** 内置名称空间,如果最终还是没有找到对应的变量,则会引发 NameError 异常。

二、 作用域

1 .全局作用域与局部作用域使用方法

  • 局部作用域:在函数内部声明变量时,该变量只能在函数内部使用。
    • 需要注意的是,在函数调用结束后,其内部定义的局部变量会被销毁,因此如果需要在函数外部访问函数内的局部变量,可以通过将该变量作为函数的返回值传递出去的方式来实现。
def my_func():
    x = 10
    return x  # 将局部变量 x 作为函数的返回值返回

    # 调用函数并获取返回值
result = my_func()
print(result)  # 输出 10
  • 全局作用域:在py 文件中声明的变量和函数等对象都属于全局作用域,在整个py 文件中都可以直接使用。该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用)
    • 如果需要在模块外部使用该变量,则需要使用 global 关键字显式地声明该变量。
x = 10  # 定义全局变量 x

def my_func():
    global x  # 声明需要使用全局变量 x
    x += 5    # 修改全局变量 x 的值
    print(x)

my_func()     # 输出 15
print(x)      # 输出 15
  • 内置作用域:Python 解释器默认载入的函数和变量等都属于内置作用域,可以在任何地方直接使用,不需要进行导入操作。
1.使用内置函数 abs() 计算绝对值
x = -10
result = abs(x)
print(result)  # 输出 10

应该尽量减少全局变量的使用,避免出现不必要的命名冲突和其他问题,同时在使用函数时,也应该注意使用参数和返回值等方式来进行变量传递和数据处理。

2.作用域与名字查找的优先级

  • 局部作用域查找:起始先从局部名称空间查找,局部名称空间找不到再去全局名称空间查找,全局名称空间找不到再去内置名称空间查找,都找不到会抛出异常
    • 局部名称空间 全局名称空间** →** 内置名称空间,如果最终还是没有找到对应的变量,则会引发 NameError 异常。
x=100 #全局作用域的名字x
def foo():
    x=300 #局部作用域的名字x
    print(x) #在局部找x
foo()#结果为300
  • 全局作用域查找:起始全局名称空间查找,全局名称空间找不到再去内置名称空间查找,都找不到会抛出异常
    • 全局名称空间** →** 内置名称空间,如果最终还是没有找到对应的变量,则会引发 NameError 异常。
x=100
def foo():
    x=300 #在函数调用时产生局部作用域的名字x
foo()
print(x) #在全局找x,结果为100
  • ps:提示:可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。
  • 在全局作用域查看到的locals()的结果等于globals()
  • Python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域(函数嵌套都属于局部名称空间)
x=1
def outer():
    x=2
    def inner(): # 函数名inner属于outer这一层作用域的名字
        x=3
        print('inner x:%s' %x)

    inner()
    print('outer x:%s' %x)

outer() 
#结果为
inner x:3
outer x:2

1.在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字
x=1
def foo():
    global x #声明x为全局名称空间的名字
    x=2
foo()
print(x) #结果为2

2.当实参的值为可变类型时,函数体内对该值的修改将直接反应到原值,
num_list=[1,2,3]
def foo(nums):
    nums.append(5)

foo(num_list)
print(num_list)
#结果为
[1, 2, 3, 5]

3.对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
def  f1():
    x=2
    def f2():
        nonlocal x
        x=3
    f2() #调用f2(),修改f1作用域中名字x的值
    print(x) #在f1作用域查看x

f1()

#结果
3

nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常。