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。

    原文作者:robinhhu
    原文地址: https://segmentfault.com/a/1190000020133312
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞