零零碎碎学着python已经好久了,但是总会发现有好些知识点都没学到,今天在牛客网看到了这样一道题:
# 下列代码的运行结果是什么?
def bar(n):
m = n
while True:
m+=1
yield m
b = bar(3)
print b.next()
题目本身没什么难度,所以就不多说了~
emmmm…刚看到题目的时候,果然发现了我印象里没有的东西——yield。迅速查了资料,大概了解到,一般情况下,带有yield的函数不是一般的函数,而是一个生成器(generator)。
yield这个关键字的作用比较类似于return和print,均有输出/返回结果之意,next()表示依次返回迭代结果。
下面,暂且不说generator,先以一个简单的例子——裴波那契数列来说一说yield。
实例1:简单输出裴波那契数列的前n个数。
def fibo(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
这样的写法属于常规操作,但是,函数的可复用性不高。因为fibo()函数没有返回值,其他函数无法获得该函数生成的数列。
这时我们会想到,为了提高函数的可复用性,我们不直接打印出数列,而是去返回一个List.
从而也就得到了下面这种做法。
实例2:
def fibo(max):
n, a, b = 0, 0, 1
L = []
while n < max:
L.append(b)
a, b = b, a + b
n = n + 1
return L
通过以上的改动,函数的可复用性大大提高,但是,该函数在运行中占用的内存会随着参数 max 的增大而增大,因此如果要控制内存占用,最好不要用 List来保存中间结果,而是通过 iterable 对象来迭代。
for i in range(1000):
pass
# 会生成一个有1000个元素的List
for i in xrange(1000):
pass
# 不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为 xrange 不返回 List,而是返回一个 iterable 对象。
利用 iterable 我们可以把 fibo() 函数改写为一个支持 iterable 的 class,这样也就有了下面的实例:
实例3:
class Fibo(object):
def __init__(self, max):
self.max = max
self.n, self.a, self.b = 0, 0, 1
def __iter__(self):
return self
# 下一个迭代对象
def next(self):
if self.n < self.max:
r = self.b
self.a, self.b = self.b, self.a + self.b
self.n += 1
return r
raise StopIteration()
for n in Fibo(5):
print n
Fibo 类通过 next() 函数不断返回数列的下一个数,内存占用始终为常数、
然而,
使用 class 改写的这个版本,代码远远没有实例1简洁。如果我们既要保持实例1的简洁性,又要获得 迭代效果,这时,yield 就派上用场了:
实例4:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
这时你就会发现,实例4与实例1的区别仅仅在于将中间的print换成了yield。
因此,简单来说,yield就是把一个普通函数变成了一个生成器。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
以上大致是今日所得,日后如果遇到相关问题,再对此文另行补充~