迭代器(Iterator)
为了理解 yield 是什么,首先要明白生成器(generator)是什么,在讲生成器之前先说说迭代器(iterator),当创建一个列表(list)时,你可以逐个的读取每一项,这就叫做迭代(iteration)。
- mylist = [ 1 , 2 , 3 ]
- for i in mylist :
- print (i)
- 1
- 2
- 3
Mylist 就是一个迭代器,不管是使用复杂的表达式列表,还是直接创建一个列表,都是可迭代的对象。
- mylist = [x*x for x in range( 3 )]
- for i in mylist :
- print (i)
- 0
- 1
- 4
你可以使用“for··· in ···”来操作可迭代对象,如:list,string,files,这些迭代对象非常方便我们使用,因为你可以按照你的意愿进行重复的读取。但是你不得不预先存储所有的元素在内存中,那些对象里有很多元素时,并不是每一项都对你有用。
生成器(Generators)
生成器同样是可迭代对象,但是你只能读取一次,因为它并没有把所有值存放内存中,它动态的生成值:
- mygenerator = (x*x for x in range( 3 ))
- for i in mygenerator :
- print (i)
- 0
- 1
- 4
使用()和[]结果是一样的,但是,第二次执行“ for in mygenerator”不会有任何结果返回,因为它只能使用一次。首先计算 0,然后计算 1,之后计算 4,依次类推。
Yield
**Yield 是关键字, 用起来像 return,yield 在告诉程序,要求函数返回一个生成 **器。
-
def createGenerator() :
-
mylist = range( 3 )
-
for i in mylist :
-
yield i*i
-
mygenerator = createGenerator() # create a generator
-
print (mygenerator) # mygenerator is an object!
-
< **generator object **createGenerator at 0xb7555c34 >
-
for i in mygenerator:
-
print (i)
-
0
-
1
-
4
这个示例本身没什么意义,但是它很清晰地说明函数将返回一组仅能读一次的值,要想掌握 yield,首先必须理解的是:当你调用生成器函数的时候,如上例中的 createGenerator(),程序并不会执行函数体内的代码,它仅仅只是返回生成器对象,这种方式颇为微妙。函数体内的代码只有直到每次循环迭代(for)生成器的时候才会运行。
函数第一次运行时,它会从函数开始处直到碰到 yield 时,就返回循环的第一个值,然后,交互的运行、返回,直到没有值返回为止。如果函数在运行但是并没有遇到 yield,就认为该生成器是空,原因可能是循环终止,或者没有满足任何”if/else”。
接下来读一小段代码来理解生成器的优点:
控制生成器穷举
-
class Bank(): # 创建银行,构造 ATM 机
- ... crisis = False
- ... def create_atm( self ) :
- ... while not self .crisis :
- ... yield "$100"
-
hsbc = Bank() # 没有危机时,你想要多少,ATM 就可以吐多少
-
corner_street_atm = hsbc.create_atm()
-
print (corner_street_atm.next())
- $ 100
-
print (corner_street_atm.next())
- $ 100
-
print ([corner_street_atm.next() for cash in range( 5 )])
- [ '$100' , '$100' , '$100' , '$100' , '$100' ]
-
hsbc.crisis = True # 危机来临,银行没钱了
-
print (corner_street_atm.next())
-
wall_street_atm = hsbc.ceate_atm() # 新建 ATM,银行仍然没钱
-
print (wall_street_atm.next())
-
hsbc.crisis = False # 麻烦就是,即使危机过后银行还是空的
-
print (corner_street_atm.next())
-
brand_new_atm = hsbc.create_atm() # 构造新的 ATM,恢复业务
-
for cash in brand_new_atm :
- ... print cash
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
- $ 100
对于访问控制资源,生成器显得非常有用。
迭代工具,你最好的朋友
**迭代工具模块包含了操做指定的函数用于操作迭代器。 **想复制一个迭代器出来?链接两个迭代器?以 one liner(这里的 one-liner 只需一行代码能搞定的任务)用内嵌的列表组合一组值?不使用 list 创建 Map/Zip?···,你要做的就是 import itertools,举个例子吧:
四匹马赛跑到达终点排名的所有可能性:
-
horses = [ 1 , 2 , 3 , 4 ]
-
races = itertools.permutations(horses)
-
print (races)
-
print (list(itertools.permutations(horses)))
- [( 1 , 2 , 3 , 4 ),
- ( 1 , 2 , 4 , 3 ),
- ( 1 , 3 , 2 , 4 ),
- ( 1 , 3 , 4 , 2 ),
- ( 1 , 4 , 2 , 3 ),
- ( 1 , 4 , 3 , 2 ),
- ( 2 , 1 , 3 , 4 ),
- ( 2 , 1 , 4 , 3 ),
- ( 2 , 3 , 1 , 4 ),
- ( 2 , 3 , 4 , 1 ),
- ( 2 , 4 , 1 , 3 ),
- ( 2 , 4 , 3 , 1 ),
- ( 3 , 1 , 2 , 4 ),
- ( 3 , 1 , 4 , 2 ),
- ( 3 , 2 , 1 , 4 ),
- ( 3 , 2 , 4 , 1 ),
- ( 3 , 4 , 1 , 2 ),
- ( 3 , 4 , 2 , 1 ),
- ( 4 , 1 , 2 , 3 ),
- ( 4 , 1 , 3 , 2 ),
- ( 4 , 2 , 1 , 3 ),
- ( 4 , 2 , 3 , 1 ),
- ( 4 , 3 , 1 , 2 ),
- ( 4 , 3 , 2 , 1 )]
理解迭代的内部机制:
迭代(iteration)就是对可迭代对象(iterables,实现了__iter__()方法)和迭代器(iterators,实现了__next__()方法)的一个操作过程。可迭代对象是任何可返回一个迭代器的对象,迭代器是应用在迭代对象中迭代的对象,换一种方式说的话就是:iterable 对象的__iter__()方法可以返回 iterator 对象,iterator 通过调用 next()方法获取其中的每一个值(译者注),读者可以结合 Java API 中的 Iterable 接口和 Iterator 接口进行类比。
(
java Iterable 接口:
public interface Iterable
Implementing this interface allows an object to be the target of the "foreach" statement.
方法:
Returns an iterator over a set of elements of type T.
Returns:
an Iterator.
Iterator 接口:
public interface Iterator
An iterator over a collection. Iterator takes the place of Enumeration in the Java collections framework. Iterators differ from enumerations in two ways:
- Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
- Method names have been improved.
This interface is a member of the Java Collections Framework .
boolean hasNext()
Returns true if the iteration has more elements.
E next()
Returns the next element in the iteration.
void remove()
Removes from the underlying collection the last element returned by the iterator (optional operation).
为什么一定要去实现 Iterable 这个接口呢? 为什么不直接实现 Iterator 接口呢?
看一下 JDK 中的集合类,比如 List 一族或者 Set 一族,
都是实现了 Iterable 接口,但并不直接实现 Iterator 接口。
仔细想一下这么做是有道理的。 **因为 Iterator 接口的核心方法 next()或者 hasNext()
是依赖于迭代器的当前迭代位置的。 **
如果 Collection 直接实现 Iterator 接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么 next()方法的结果会变成不可预知。
除非再为 Iterator 接口添加一个 reset()方法,用来重置当前迭代位置。
但即时这样,Collection 也只能同时存在一个当前迭代位置。
而 Iterable 则不然,每次调用都会返回一个从头开始计数的迭代器。
多个迭代器是互不干扰的
来源 :http://blog.csdn.net/fish0058/article/details/23199249
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于