Python变量作用域

发布于 2021 年 08 月 02 日 | 更新于 2021 年 08 月 17 日

名字 symbolic name

An assignment statement creates a symbolic name that you can use to reference an object. The statement x = 'foo' creates a symbolic name x that refers to the string object 'foo'.

命名空间 Namespaces

A namespace is a collection of currently defined symbolic names along with information about the object that each name references. You can think of a namespace as a dictionary in which the keys are the object names and the values are the objects themselves. Each key-value pair maps a name to its corresponding object.

命名空间是一组名字及其所指对象的容器,可理解成字典,名字为键(key),对象为值(value)

在一个命名空间中,名字是唯一的,而不同命名空间中可以有相同名字

不同的命名空间把相同名字不同对象隔开,解决了名字冲突的问题

变量作用域 Variable Scope

The existence of multiple, distinct namespaces means several different instances of a particular name can exist simultaneously while a Python program runs. As long as each instance is in a different namespace, they’re all maintained separately and won’t interfere with one another.

命名空间解决了名字冲突的问题,而变量作用域则规定了查找名字的规则LEGB(从哪个命名空间中查找名字),划分了访问名字的权限(外部作用域无法访问内部作用域的名字)

我感觉命名空间和变量作用域在很多时候是一个意思那到底有什么区别呢?

只有模块(module),类(class),函数(def, lambda)会产生新的命名空间,其他如流程控制的代码块(if, for, try等)不会产生新命名空间。产生新命名空间意味着外部无法访问新命名空间内定义的变量

从外到内分为4种命名空间:

  1. Built-in(内建):python自带的变量
  2. Global or module(全局)
  3. Enclosing or nonlocal(闭包):既不是全局也不是局部,通常出现在闭包语境下
  4. Local or function(局部)

查找一个名字是否可用的顺序是:L->E->G->B(LEGB rule),区域由内到外

global & nonlocal

改变默认的LEGB规则:在内部空间使用,指定访问哪个命名空间的名字,同时赋予修改权限(允许修改外部变量)

内部作用域虽然可以自由访问外部作用域的变量值,却无法直接修改

想在内部作用域修改外部作用域的变量时,就需要用globalnonlocal关键字声明一下:

gv = 1     # 全局变量--不可变数据类型
gvs = [1]  # 全局变量--可变数据类型
def outer():
    global gv
    gv += 2
    gvs[0] += 2

    global lazy_gv
    lazy_gv = 10

    nlv = 11     # 非局部变量--不可变数据类型
    nlvs = [11]  # 非局部变量--可变数据类型
    def inner():
        nonlocal nlv
        nlv += 22
        nlvs[0] += 22

    print(nlv, nlvs)  # 11 [11]
    inner()
    print(nlv, nlvs)  # 33 [33]

print(gv, gvs, ('lazy_gv' in globals()))  # 1 [1] False
outer()
print(gv, gvs, ('lazy_gv' in globals()))  # 3 [3] True

对于可变数据类型,由于没有修改容器本身(容器本身的 id没变),所以不声明也能修改容器内的值

获取指定作用域内的变量:

globals():获取所有全局变量,还可以修改变量值(字典

locals():在函数内调用时,获取函数内所有局部变量(字典

vars([object]):返回object__dict__属性(字典),不传参数返回locals()

dir([object]):返回object命名空间中的名字(列表),"__dict__"是其中之一;不传参数返回当前命名空间中的名字(列表

__dict__:存放对象的可写属性,可以通过该字典修改对象属性

总结

内部命名空间可以访问外部命名空间的名字,但不能修改,要修改需要加globalnonlocal

外部空间不能访问内部变量

Python Scope & the LEGB Rule: Resolving Names in Your Code – Real Python

Namespaces and Scope in Python – Real Python

Python3 命名空间和作用域 – runoob