笔记-python-变量作用域

 

1.      python变量作用域和引用范围

1.1.    变量作用域

一般而言程序的变量并不是任何对象或在任何位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个变量。

Python的作用域一共有4种,分别是:

L (Local) 局部作用域

E (Enclosing) 闭包函数外的函数中

G (Global) 全局作用域

B (Built-in) 内建作用域

以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问,如下代码:

 

1.2.    全局变量和局部变量作用域

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:

# 全局变量和局部变量

total = 10
 
print(total, id(total))
def sum(arg1, arg2):
    total = arg1 + arg2
    print(total, id(total))
    return total
 
c = sum(34, 12)
print(c, total, id(total))

输出:

10 1635020544

46 1635021696

46 10 1635020544

可以看出函数内外的total并非指向同一地址。

1.3.    global and nonlocal

在函数内部是可以访问全局变量的,但有时需要在函数内部修改全局变量,又不想使用列表,这时,就要用到global 和nonlocal。

# global  and nonlocal
 
num = 1
 
def fun1():
    global num
    print(num, id(num))
    num = 123
    print(num, id(num))
 
fun1()
print(num, id(num))

输出:

1 1635020256

123 1635024160

123 1635024160

可以发现变量num被修改,引用对象发生了变化。

 

如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下实例:

# nonlocal
 
def outer():
    num = 10
    print(num, id(num))
    def inner():
        nonlocal num
        num = 100
        print(num, id(num))
    inner()
    print(num, id(num))
 
outer()

输出:

10 1635020544

100 1635023424

100 1635023424

 

1.4.    nonlocal

在python文档中对nonlocal的解释如下:

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*

The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Names listed in a nonlocal statement must not collide with pre-existing bindings in the local scope.

需要注意的是nonlocal无法引用全局变量,下面的代码会报错,找不到num变量。

# nonlocal
num = 19
def outer():
    #num = 10
    print(num, id(num))
    def inner():
        nonlocal num
        num = 100
        print(num, id(num))
    inner()
    print(num, id(num))
 
outer()

 

2.      附

2.1.    一些报错的写法

有一种情况,假设下面这段代码被运行:

不讨论它是如何具体发生的,但显然,这样的写法引发了冲突,报错是正常的。

a = 10
def test():
    a = a + 1
    print(a)
test()

以上程序执行,报错信息如下:

……
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。

下面是另一种类似错误:

a = 10
 
def fun1():
    print(a)
    a = 15
 
fun1()

输出:

    print(a)

UnboundLocalError: local variable 'a' referenced before assignment