乐趣区

python-函数-变量和闭包

首先我们认定,python 中定义域查找遵循 local->Enclosing->Global->Built-in 顺序:

a=1
def func1():
… print(a)
… a=111
… print(a)

func1()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “<stdin>”, line 2, in func1
UnboundLocalError: local variable ‘a’ referenced before assignment

而:

a=1
def fun():
… print(a)
… b=111
… print(b)

fun()
1
111
print(b)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘b’ is not defined

我们可以得出结论(打脸):内置函数先在内置函数定义域内(前后)寻找变量;找不到之后再从全局变量中引进,且局部变量无法全局。
如果 global:

a=1
def func1():
… global a
… print(a)
… a=111
… print(a)

func1()
1
111
a
111

但是不多久后我发现一个问题,代码如下:

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

test()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “<stdin>”, line 2, in test
UnboundLocalError: local variable ‘a’ referenced before assignment
test(a)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: test() takes 0 positional arguments but 1 was given
所以这个问题其实可以被拆分为两个问题,一个是 arguments 的问题,还有一个是 variable 的问题。
当定义了一个 argument 的时候只要在括号里把 global 中的 variable 代入就是可以运行的,前提就是可一定要定义啊!!!

a=1
def func1(a):
… print(a)
… a=111
… print(a)

func1(a)
1
111

也就是说问题根本不在 global,而是我有没有把 a 带进去 …(哭泣)
结论就是一切都是我自作多情想多了,自己的 bug

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

test(a)
10
11

所以回到第一个例子,所谓的“local variable referred before assignment”只是因为我没有把变量在定义的时候放进去。

这是第一件事

第二件:只有模块,类以及函数才会引入新的定义域,其他代码块如(if/else,while)是不会的:

a=0
while a < 4:
… print(a)
… a += 1

0
1
2
3
a
4

三:嵌套和闭包

def out():
… a=7
… def inner():
… nonlocal a
… print(a)
… a=9
… print(a)
… inner()
… print(a)

out()
7
9
9

嵌套和 nonlocal 都超好理解
让我斯巴达的是如下:

def fun2(a):
… print(a)
… def fun3(b):
… print(a,b)
… return fun3 #返回 fun3 函数结果

fun2(1)
1
<function fun2.<locals>.fun3 at 0x000001E2857C24C8>
f=fun2(1)
1
f
<function fun2.<locals>.fun3 at 0x000001E2857A4828>
f(4)
1 4

嗯这就是传说中的闭包,闭包使得函数内部的变量可以一直被保存并且被外部使用(像个自由的包裹一直裹着里面的变量)
为了更直观一点:

def out():
… def inner():
… a=5
… return a
… inner()
… return inner

f=out()
f
<function out.<locals>.inner at 0x000001E2857A4678>
f()
5

可见调用的这个定义函数,返回的仍旧是一个函数,而不是一个值。out()不是一个函数运行结果而是一个由返回的 inner 函数和变量 a 构成的函数(因为闭包的本质就是一种函数,由局部变量和内部函数构成)。
具体一点说来,在第一个例子中,运行 fun2(1)将同时得到 print 出来的一个 a,和一个以 fun3 为函数,被保留的 a 和未被赋值的 b 为变量的函数。【当定义符合闭包条件时,自由变量(此处的 f)变成一个闭包类,有函数的效果】。
至于为什么它的地址在变化,我觉得是因为它每次调用都返回了一个新函数(分开储存)。

233333 我又看到了一个神奇东西

def count():
… fs=[]
… for i in range(1,4):
… def f():
… return i*i
… fs.append(f)
… return fs

f1,f2,f3=count()
f1
<function count.<locals>.f at 0x000001E2857A4438>
f1()
9
f2()
9
f3()
9

此处函数为闭包的原因在于 append 的那个 f,如果我做一个改动

def count():
… fs=[]
… for i in range(1,4):
… def f():
… return i*i
… fs.append(f())
… return fs

count()
[1, 4, 9]

它就不是闭包了,count 可以正常输出结果。
而在这里,返回的函数是 i *i,但是由于返回时 i =3,f1,f2,f3 都变成了 9。

退出移动版