变量作用域问题
case1
通常在函数之外无法访问到函数内的变量
def f1():
a=1
print(a)
NameError: name 'a' is not defined
case2
但函数内部的函数,是可以访问到外部函数的变量的。
def f1():
a=1
def f2():
print(a)
f2()
f1()
1
case3
但对于int tuple str 等不可变变量,内部变量只能访问不能修改。
def f1():
a=1
def f2():
a+=1
print(a)
f2()
f1()
Traceback (most recent call last):
File "xxx.py", line 11, in <module>
f1()
File "xxx.py", line 10, in f1
f2()
File "xxx.py", line 8, in f2
a+=1
UnboundLocalError: local variable 'a' referenced before assignment
这个报错的逻辑大概是 能修改的是一个局部变量a, 你在修改一个变量a,但并没有这个局部变量a, 对于f2来说, 这个a是一个外部变量而不是局部变量。
case4
因此我们可以重新在函数内部中定义一个局部变量a,之后我们的操作都是对这个局部变量的操作了,与外部变量无关。
def f1():
a = 1
def f2():
a = 1
a += 1
print('内层函数:', a)
f2()
print('外层函数:', a)
f1()
内层函数: 2
外层函数: 1
case5
case3这提到了不可变变量,对应list dic等可变变量来说,其实能改变的也是其内部的值而不是变量本身。可以类比于c++中的常量指针,可以修改指针指向的地址的值。
def f1():
a = [1]
def f2():
a[0] += 1
print('内层函数:', a[0])
f2()
print('外层函数:', a[0])
f1()
内层函数: 2
外层函数: 2
闭包
在case2中,既然f2可以访问到f1的内部变量,那么如果将f2作为f1的返回值,自然就可以通过f2在外部访问到f1的内部变量。
def f1():
a=1
def f2():
return a
return f2
f2=f1()
print(f2())
1
这个f2
就是闭包。具体可以参考下面这篇文章,本篇不展开讨论。
nonlocal
针对case3,如果我们就是想在内部函数中修改变量a,可以借助nonlocal
关键字。
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。可以理解为,使用nonlocal关键字,内层可以将外层变量看做本地变量,赋予了内层函数对外层函数变量的修改权。
case6
def f1():
a=1
def f2():
nonlocal a
a+=1
print('内层函数:', a)
f2()
print('外层函数:', a)
f1()
内层函数: 2
外层函数: 2
case7
因此必须外部有这个变量才能使用此关键字。
def f1():
# a=1
def f2():
nonlocal a
a+=1
print('内层函数:', a)
f2()
print('外层函数:', a)
f1()
nonlocal a
^
SyntaxError: no binding for nonlocal 'a' found
global
global
的作用是将变量声明为全局变量,即内部变量允许外部访问
case8
def f1():
a = 1
def f2():
global a
a=1
a += 1
print('内层函数:', a)
f2()
print('外层函数:', a)
f1()
print('外部:',a)
内层函数: 2
外层函数: 1
外部: 2
case9
def f1():
# a = 1
def f2():
global a
a=1
a += 1
print('内层函数:', a)
f2()
print('外层函数:', a)
f1()
print('外部:',a)
内层函数: 2
外层函数: 2
外部: 2
对比case8和case9,在7中,由于外层函数声明了变量a,因此会优先使用局部变量。而在case8中,全部都是使用的内层函数中声明的全局变量。