Python中幽灵般的yield

来源:互联网 发布:会计事务所审计软件 编辑:程序博客网 时间:2024/04/30 20:53

Python中幽灵般的yield

Python语言容易上手,偶尔也容易伤手,比如yield的用法。

这篇短文希望用简单的例子把yield的本质讲清楚,只是希望而已,万一讲不清楚,你可以用红包砸我~,:-x
直接说本文最重要的一句话:

python函数定义里用了yield就会把函数变为生成器。

例子#1 - 正常函数定义、调用

代码 try.py:
定义一个函数func,然后调用它,该函数返回1

#!/usr/bin/env pythondef func():    print ("func() started")    return 1    print ("func() ended")g = func()print "g = ", g

输出结果:

$ ./try.pyfunc() startedg =  1

这个没什么需要解释的。

例子#2 - 把例子#1里函数定义的return改成yield

#!/usr/bin/env pythondef func():    print ("func() started")    yield 1    print ("func() ended")g = func()print "g = ", g

输出结果:

$ ./try.pyg =  <generator object func at 0x10ccbcaa0>
  • 发现没?func这个“函数”竟然没有被调用!func的返回结果g的类型变成了生成器(generator)对象。

  • 因为“函数”func的定义里用了yield,导致func的类型不再是函数,而是变成了生成器类型定义,这时候“调用”func等于创建一个generator对象g,并不会执行里面的代码。

说明:
  • “函数定义”里一旦用了yield,就不再表示定义函数,而是变成定义生成器类型。这是动态类型语言python的特点。
  • 另外,“函数”定义里有return语句就是函数,有yield就是生成器,return和yield不能同时使用,要么你是定义函数,要么你是定义生成器,只能二选一。

  • 理解了yield必然导致生成器,就抓住了yield的本质,要运行生成器func里的代码就必须对它的对象进行遍历。

  • 生成器是只能遍历一次的迭代器,而常用的列表是可以重复从头多次遍历的,生成器遍历到尾部就不能再从头遍历了。

最后例子 - 遍历yield定义的生成器

#!/usr/bin/env pythondef func():    print ("func() started")    for i in range(0,3):        yield i #每次到这里都生成一个迭代元素    print ("func() ended")g = func() #这里不会执行func里的代码,只是创建了一个对象(generator对象)print "g = ", gfor val in g: #遍历生成器,执行func里生成一个个迭代元素的代码,等价于执行了func里面的代码    print "    val=", val

输出结果:

$ ./try.pyg =  <generator object func at 0x10a6e2aa0>func() started    val= 0 #生成的一个元素    val= 1 #生成的一个元素    val= 2 #生成的一个元素func() ended #遍历的时候扫过这一行,所以它执行到了,但是它后面没有生成的元素了,到尾部了
0 0