用的较小整数值(-5到256)创建好了对象,因为它们使用的非常频繁(有些在python的内部已经使用了)。所以,对于这个范围内的整数,都是直接引用,不会再在内存中额外创建新的数值对象,所以x is y
总是返回true。甚至,这些小值整数可以跨作用域:
x = 3
def f1():
y=3
print(x is y) # True
f1()
再看前文循环内的函数的问题。
def f1():
for i in range(5):
def n():
print(i)
return n
f1()()
前面对现象已经解释过,内部函数n()中print(i)的i不会随循环的迭代而改变,而是固定的值i=4。
python首先解释def f1()
的代码块,会记录属于这个代码块作用域内的变量i和n,但i和n都不会赋值,也就是说暂时并不知道变量n是一个函数变量。
同理,当需要解释def n()
代码块的时候,将记住这个代码块涉及到的变量i,只不过这个变量i是属于外层函数的,但不管如何,这个代码块记住了i,且记住了它是外部函数作用域的。
注意,函数的声明过程中,所有涉及到变量i的作用域内都不会对i进行赋值,仅仅只是保存了这个i变量名,只有在调用函数的时候才会进行赋值操作。
当开始调用f1()的时候,开始执行函数体中的代码,于是开始循环迭代,且多次声明函数n()
,每一次迭代生成的n()都会让原先已记录的变量n指向这个新声明的函数体(相当于赋值的操作,只不过是变量n引用的对象是函数体结构,而不是一般的数据对象),由于只是在循环中声明函数n(),并没有进行调用,所以不会对n()中的i进行赋值操作。而且,每次循环迭代都会让变量n指向新的函数体,使得先前迭代过程中定义的函数被丢弃(覆盖),所以最终只记住了最后一轮循环时声明的函数n(),并且i=4。
当调用f1()()时,表示调用f1()中返回的函数n(),直到这个时候才会对n()内的i进行赋值,赋值时将搜索它的外层函数f1()作用域,发现这个作用域内的i指向内存中的数值4,于是最终输出4。
再看下面的代码:
def f1():
for i in range(5):
def n():
print(i)
n()
return n
f1()
输出结果:
0
1
2
3
4
调用f1()的时候,执行循环的迭代,每次迭代时都会调用n(),意味着每次迭代都要对n()中的i进行赋值。
另外注意,前面说过,函数的默认参数是在函数声明时进行赋值的,所以下面的列表L中每个元素所代表的函数,它们的变量i都指向不同的数值对象。
def f1():
L = []
for i in range(5):
def n(i=i):
print(i)
L.append(n)
return L
f1()[0]()
f1()[1]()
f1()[2]()
f1()[3]()
f1()[4]()
执行结果:
0
1
2
3
4