python 函数 变量和闭包

robinhhu 2019-08-23 15:20:07
原文地址:https://segmentfault.com/a/1190000020133312

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

a=1
def func1():
... print(a)
... a=111
... print(a)
...
func1()
Traceback (most recent call last):
File "", line 1, in
File "", 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 "", line 1, in
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 "", line 1, in
File "", line 2, in test
UnboundLocalError: local variable 'a' referenced before assignment
test(a)
Traceback (most recent call last):
File "", line 1, in
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..fun3 at 0x000001E2857C24C8>
f=fun2(1)
1
f
<function fun2..fun3 at 0x000001E2857A4828>
f(4)
1 4

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

def out():
... def inner():
... a=5
... return a
... inner()
... return inner
...
f=out()
f
<function out..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..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。

声明:该文章系转载,转载该文章的目的在于更广泛的传递信息,并不代表本网站赞同其观点,文章内容仅供参考。

本站是一个个人学习和交流平台,网站上部分文章为网站管理员和网友从相关媒体转载而来,并不用于任何商业目的,内容为作者个人观点, 并不代表本网站赞同其观点和对其真实性负责。

我们已经尽可能的对作者和来源进行了通告,但是可能由于能力有限或疏忽,导致作者和来源有误,亦可能您并不期望您的作品在我们的网站上发布。我们为这些问题向您致歉,如果您在我站上发现此类问题,请及时联系我们,我们将根据您的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。