翻了一篇workflow上关于yield的用法,翻的有点烂,在这里贻笑大方了,慢慢来,总是期待着一点一点的进步。
为了理解yield的机制,我们需要理解什么是生成器。在此之前先介绍迭代器iterables。
当你创建一个list,你可以一个一个的获取,这种列表就称为迭代:
>>> mylist = [1, 2, 3] >>> for i in mylist: ... print(i) 1 2 3Mylist 是一个迭代器. 当你理解它为一个list,它便是可迭代的:
>>> mylist = [x*x for x in range(3)] >>> for i in mylist: ... print(i) 0 1 4
任何可以用 for in 来迭代读取的都是迭代容器,例如lists,strings,files.这些迭代器非常的便利,因为你可以想取多少便取多少,但是你得存储所有的值,其中很多值都完全没有必要每次都保持在内存中。
Generators(生成器)也是可迭代的,但是你每次只能迭代它们一次,因为不是所有的迭代器都被一直存储在内存中的,他们临时产生这些值:
>>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ... print(i) 0 1 4
生成器几乎和迭代器是相同的,除了符号[]变为()。但是你无法用两次,因为他们只生成一次:他们生成0然后丢弃,继续统计1,接着是4,一个接着一个。
Yield的用法有点像return,除了它返回的是一个生成器,例如:
>>> 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()生成的是一个生成器。
为了掌握yield的精髓,你一定要理解它的要点:当你调用这个函数的时候,你写在这个函数中的代码并没有真正的运行。这个函数仅仅只是返回一个生成器对象。有点过于奇技淫巧:-)
然后,你的代码会在每次for使用生成器的时候run起来。
现在是解释最难的地方:
当你的for第一次调用函数的时候,它生成一个生成器,并且在你的函数中运行该循环,知道它生成第一个值。然后每次调用都会运行循环并且返回下一个值,知道没有值返回为止。该生成器背认为是空的一旦该函数运行但是不再刀刀yield。之所以如此是因为该循环已经到达终点,或者是因为你再也不满足“if/else”的条件。
调用者:
# 创建一个空的列表Create an empty list and a list with the current object reference result, candidates = list(), [self] # 循环candidates列表,只有一个元素。Loop on candidates (they contain only one element at the beginning) while candidates: # Get the last candidate and remove it from the list node = candidates.pop() # Get the distance between obj and the candidate distance = node._get_dist(obj) # If distance is ok, then you can fill the result if distance <= max_dist and distance >= min_dist: result.extend(node._values) # Add the children of the candidate in the candidates list # so the loop will keep running until it will have looked # at all the children of the children of the children, etc. of the candidate candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result这段代码包含一些非常机智的部分:
1. list的循环迭代部分,但是list在循环的同时又在拓展,:)这种方法是一种循环内嵌式的数据的相对简洁的方法,但是又存在着一些风险可能会导致死循环的情况。在这个例子当中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 耗尽所有的的生成器的值,但是当保持生成新的生成器对象,并且依据之前生成器产生许多不同的值,由于它产生于不同的节点。
2. extend()方法是一个list 对象方法,它产生一个迭代器并且添加它的值到list当中去。
通常我们
>>> a = [1, 2] >>> b = [3, 4] >>> a.extend(b) >>> print(a) [1, 2, 3, 4]
但是代码中获得一个生成器,这种方式比较好的原因如下:
首先是你无须读取该值两次。
然后你不需要把所有的值都放在内存中。
与此同时,它能够owrk的原因是Python不关心一个方法的参数石佛是一个list.期待是一个迭代器所以它能够适用于strings,lists,tuples以及生成器。这被称为动态类型或者鸭子类型(duck typing)是python 如此酷的一大原因。鸭子类型又是另外一个问题了,blablabla。
现在让我们来看看一些高级的用法:
控制生成器资源消耗:
>>> class Bank(): # let's create a bank, building ATMs ... crisis = False ... def create_atm(self): ... while not self.crisis: ... yield "$100" >>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want >>> 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 # crisis is coming, no more money! >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs >>> print(wall_street_atm.next()) <type 'exceptions.StopIteration'> >>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business >>> for cash in brand_new_atm: ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ...这一个非常的有用,特别是类似的资源访问控制。
Itertools模块包含一些特别的函数去执行迭代器。有没有想过去复制一个生成器 或者链接两个生成器?等等。
引入itertools就好了,import itertools.
下面举个例子.看看四匹马到达先后顺序的例子:
>>> horses = [1, 2, 3, 4] >>> races = itertools.permutations(horses) >>> print(races) <itertools.permutations object at 0xb754f1dc> >>> 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 is a process implying iterables (implementing the __iter__() method) and iterators (implementing the __next__() method). Iterables are any objects you can get an iterator from. Iterators are objects that let you iterate on iterables.
更多的相关内容可以阅读 循环如何工作.
