Python关键字yield详解

开发 后端
此文由伯乐在线 –刘志军编译自stackoverflow Python标签中投票率最高的一个问题《The Python yield keyword explained》,e-satis 详细回答了关于yield 以及 generator、iterable、iterator、iteration之间的关系。

迭代器(Iterator)

为了理解yield是什么,首先要明白生成器(generator)是什么,在讲生成器之前先说说迭代器(iterator),当创建一个列表(list)时,你可以逐个的读取每一项,这就叫做迭代(iteration)。

  1. mylist = [123]   
  2.  for i in mylist :   
  3.  print(i)   
  4. 1 
  5. 2 
  6. 3 

Mylist就是一个迭代器,不管是使用复杂的表达式列表,还是直接创建一个列表,都是可迭代的对象。

  1. mylist = [x*x for x in range(3)]   
  2. for i in mylist :   
  3. print(i)   
  4. 0 
  5. 1 
  6. 4 

你可以使用“for··· in ···”来操作可迭代对象,如:list,string,files,这些迭代对象非常方便我们使用,因为你可以按照你的意愿进行重复的读取。但是你不得不预先存储所有的元素在内存中,那些对象里有很多元素时,并不是每一项都对你有用。

生成器(Generators)

生成器同样是可迭代对象,但是你只能读取一次,因为它并没有把所有值存放内存中,它动态的生成值:

  1. mygenerator = (x*x for x in range(3))   
  2. for i in mygenerator :   
  3. print(i)   
  4. 0 
  5. 1 
  6. 4 

使用()和[]结果是一样的,但是,第二次执行“ for in mygenerator”不会有任何结果返回,因为它只能使用一次。首先计算0,然后计算1,之后计算4,依次类推。

Yield

Yield是关键字, 用起来像return,yield在告诉程序,要求函数返回一个生成器。

  1. def createGenerator() :   
  2. mylist = range(3)   
  3. for i in mylist :   
  4. yield i*i   
  5.     
  6. mygenerator = createGenerator() # create a generator   
  7. print(mygenerator) # mygenerator is an object!   
  8. <generator object createGenerator at 0xb7555c34>   
  9. for i in mygenerator:   
  10. print(i)   
  11. 0 
  12. 1 
  13. 4 

这个示例本身没什么意义,但是它很清晰地说明函数将返回一组仅能读一次的值,要想掌握yield,首先必须理解的是:当你调用生成器函数的时候,如上例中的createGenerator(),程序并不会执行函数体内的代码,它仅仅只是返回生成器对象,这种方式颇为微妙。函数体内的代码只有直到每次循环迭代(for)生成器的时候才会运行。

函数***次运行时,它会从函数开始处直到碰到yield时,就返回循环的***个值,然后,交互的运行、返回,直到没有值返回为止。如果函数在运行但是并没有遇到yield,就认为该生成器是空,原因可能是循环终止,或者没有满足任何”if/else”。

接下来读一小段代码来理解生成器的优点:

控制生成器穷举

  1. >>> class Bank(): # 创建银行,构造ATM机   
  2. ...    crisis = False 
  3. ...    def create_atm(self) :   
  4. ...        while not self.crisis :   
  5. ...            yield "$100" 
  6. >>> hsbc = Bank() # 没有危机时,你想要多少,ATM就可以吐多少   
  7. >>> corner_street_atm = hsbc.create_atm()   
  8. >>> print(corner_street_atm.next())   
  9. $100 
  10. >>> print(corner_street_atm.next())   
  11. $100 
  12. >>> print([corner_street_atm.next() for cash in range(5)])   
  13. ['$100''$100''$100''$100''$100']   
  14. >>> hsbc.crisis = True # 危机来临,银行没钱了   
  15. >>> print(corner_street_atm.next())   
  16. <type 'exceptions.StopIteration'>   
  17. >>> wall_street_atm = hsbc.ceate_atm() # 新建ATM,银行仍然没钱   
  18. >>> print(wall_street_atm.next())   
  19. <type 'exceptions.StopIteration'>   
  20. >>> hsbc.crisis = False # 麻烦就是,即使危机过后银行还是空的   
  21. >>> print(corner_street_atm.next())   
  22. <type 'exceptions.StopIteration'>   
  23. >>> brand_new_atm = hsbc.create_atm() # 构造新的ATM,恢复业务   
  24. >>> for cash in brand_new_atm :   
  25. ...    print cash   
  26. $100 
  27. $100 
  28. $100 
  29. $100 
  30. $100 
  31. $100 
  32. $100 
  33. $100 
  34. $100 

对于访问控制资源,生成器显得非常有用。

迭代工具,你***的朋友

迭代工具模块包含了操做指定的函数用于操作迭代器。想复制一个迭代器出来?链接两个迭代器?以one liner(这里的one-liner只需一行代码能搞定的任务)用内嵌的列表组合一组值?不使用list创建Map/Zip?···,你要做的就是 import itertools,举个例子吧:

四匹马赛跑到达终点排名的所有可能性:

  1. >>> horses = [1234]   
  2. >>> races = itertools.permutations(horses)   
  3. >>> print(races)   
  4. <itertools.permutations object at 0xb754f1dc>   
  5. >>> print(list(itertools.permutations(horses)))   
  6. [(1234),   
  7.  (1243),   
  8.  (1324),   
  9.  (1342),   
  10.  (1423),   
  11.  (1432),   
  12.  (2134),   
  13.  (2143),   
  14.  (2314),   
  15.  (2341),   
  16.  (2413),   
  17.  (2431),   
  18.  (3124),   
  19.  (3142),   
  20.  (3214),   
  21.  (3241),   
  22.  (3412),   
  23.  (3421),   
  24.  (4123),   
  25.  (4132),   
  26.  (4213),   
  27.  (4231),   
  28.  (4312),   
  29.  (4321)] 

理解迭代的内部机制:

迭代(iteration)就是对可迭代对象(iterables,实现了__iter__()方法)和迭代器(iterators,实现了__next__()方法)的一个操作过程。可迭代对象是任何可返回一个迭代器的对象,迭代器是应用在迭代对象中迭代的对象,换一种方式说的话就是:iterable对象的__iter__()方法可以返回iterator对象,iterator通过调用next()方法获取其中的每一个值(译者注),读者可以结合Java API中的 Iterable接口和Iterator接口进行类比。

英文原文:The Python yield keyword explained

原文链接:http://blog.jobbole.com/32748/

责任编辑:张伟 来源: 伯乐在线
相关推荐

2023-12-11 13:59:00

YieldPython生成器函数

2009-12-18 11:37:54

Ruby关键字yiel

2019-08-29 09:11:38

Pythonyield语法

2009-09-02 09:24:03

C# this关键字

2021-02-01 13:10:07

Staticc语言UNIX系统

2011-06-14 13:26:27

volatile

2022-11-12 18:32:50

Golangomitemptyjson

2018-04-20 15:56:09

Pythonglobal关键字

2009-09-28 11:34:49

Javascript

2021-07-27 07:31:16

单例模式关键字

2022-05-06 08:32:40

Pythonwith代码

2024-03-15 11:52:03

C++关键字编程

2023-03-09 07:38:58

static关键字状态

2017-05-27 20:59:30

Java多线程synchronize

2010-02-05 15:51:06

C++ explici

2023-11-10 09:29:30

MySQLExplain

2009-08-26 09:58:22

C#关键字

2020-08-23 11:03:24

Python开发void

2022-01-04 16:35:42

C++Protected关键字

2009-09-17 09:30:00

Linq LET关键字
点赞
收藏

51CTO技术栈公众号