在python中,当作用域与嵌套函数、默认参数,扯在一起的时候,它们的关系变得复杂,相互关联。下面,将该问题用代码来验证说明,以便理清相互间的关系。
1、在《Python学习手册》第431、432页(下面引为‘书中’),有一段是关于–作用域与带有循环变量的默认参数相比较–,咱们就本着这一段代码来理解这些关系。这两页的内容如下,看客可以先预览一下:
2、先在此说明几点:
a)、作用域的搜索规则:LEGB,即local->enclosing->global->built in。
b)、默认参数,实际上已改变了变量的作用域,俗称绑定。此处是重点,也是全文的核心。
c)、任何赋值语句,均会决定变量的作用域,包括import,=,def,类定义等等。
3、如书中第一段代码所示:
>>> def makeActions():
acts = []
for i in range(5):
acts.append(lambda x: i**x)
return acts
‘i**x’中的i(引为后i)由于在’for i in range(a)’(引为前i)中搜索到,所以作用域上解释是LEGB中的E,也就是lambda函数的上层函数中的i。也就是说,’前i’和’后i’是同个对象名,并且指向相同对象。
如下代码id()可以查看变量的内存地址,代码可以展示’前i’和’后i’的内存关系:
>>> def makeActions():
acts = []
for i in range(5):
print(id(i))
acts.append(lambda x: (i**x,id(i)))
return acts
>>> f=makeActions()
1415357504
1415357536
1415357568
1415357600
1415357632
>>> f[0](2)
(16, 1415357632)
>>> f[1](3)
(64, 1415357632)
>>> f[2](2)
(16, 1415357632)
由上述代码可以看出acts[]中所有lambda函数在调用时,i都为4。表明“’前i’和’后i’是同个变量名,指向相同对象”的结论是正确的。
所以在无默认参数情况下,acts[0]中的i=0,acts[1]中的i=1…的想法都是错的。
4、如书中第二段代码所示:
>>> def makeActions():
acts = []
for i in range(5):
acts.append(lambda x,i=i: i**x)
return acts
此段代码应用了“默认参数”,如本文第2点所述,默认参数实际上就是个赋值语句,已经改变了变量i的作用域。
a)、在”lambda x,i=i:i**x“表达式中第1、3个i是同个i(引为前i),第2个i是另一个i(引为后i)。
b)、由于赋值语句作用,’前i’的作用域实际上是lambda函数的本地变量。而’后i’是’for i in range(5)’中的i。
c)、也就是说,’前i’和’后i’是同个变量名,但是指向不同对象。
下面代码可以展示’前i’和’后i’的内存关系:
>>> def makeActions():
acts = []
for i in range(5):
print(id(i))
acts.append(lambda x,i=i: (i**x,id(i)))
return acts
>>> f=makeActions()
1415357504
1415357536
1415357568
1415357600
1415357632
>>> f[0](2)
(0, 1415357504)
>>> f[1](3)
(1, 1415357536)
>>> f[2](2)
(4, 1415357568)
由上述代码可以看出acts[]中所有lambda函数在调用时,i分别指向不同对象。表明“’前i’和’后i’是同个变量名,但指向不同对象”的结论是正确的。
所以在引用默认参数情况下,acts[0]中的i=0,acts[1]中的i=1…的想法都是正确的的。
5、书中第二段代码由于i的作用域问题,让人很容易混乱,若改成下方代码,便一目了然:
>>> def makeActions():
acts = []
for i in range(5):
print(id(i))
acts.append(lambda x,y=i: (y**x,id(y)))
return acts
>>> f=makeActions()
1415357504
1415357536
1415357568
1415357600
1415357632
>>> f[0](2)
(0, 1415357504)
>>> f[1](3)
(1, 1415357536)
>>> f[2](2)
(4, 1415357568)
此段代码,不仅从作用域上表明了结果,更加从变量名直观表明,所以,代码还是要清晰,否则逻辑便是混乱的,再牛逼的高手也有写错’the’和’teh’的时候。
注:以上代码,均是在python 3.6.0中运行后摘出。图片摘自python教程,仅供学习参考,如若侵权,请及时联系本人撤销。