要理解其yield作用,必须理解什么是 生成器。而且,理解生成器之前,必须理解 _iterables_。

可迭代: iterable

创立一个列表,天然是须要能一一浏览其中每个元素。逐个读取其项的过程被称为迭代:

>>> mylist = [1, 2, 3]>>> for i in mylist:...    print(i)123 

mylist 是一个_可迭代的_。当您应用列表推导式时,即是创立了一个列表,因而也是可迭代的:

>>> mylist = [x*x for x in range(3)]>>> for i in mylist:...    print(i)014 

所有能够应用 for... in... 的数据结构都是可迭代的;listsstrings,文件...

这些可迭代的办法很不便,因为您能够随便读取它们,然而您将所有值都存储在内存中,当领有很多值时,这并不总是想要的。

生成器:generator

生成器也是一种迭代器,一种非凡的迭代,非凡在只能迭代一次。生成器不会将所有值存储在内存中,而是即时生成值

generator: 发电机, 发生器,发电机发电但不储能 ;)
>>> mygenerator = (x*x for x in range(3))>>> for i in mygenerator:...    print(i)014 

只有应用()代替[], 便是列表推导式变成了生成器推导式。然而,因为生成器只能应用一次,因而您无奈执行for i in mygenerator第二次:生成器计算0,而后将其抛弃,而后计算1,最初一次计算4。典型的黑瞎子掰苞米。

让出:yield

yield关键字与return的应用形式一样,不同之处在于该函数将返回生成器

>>> def createGenerator():...    mylist = range(3)...    for i in mylist:...        yield i*i...>>> mygenerator = createGenerator() # 创立一个 generator>>> print(mygenerator) # mygenerator 是个对象!<generator object createGenerator at 0xb7555c34>>>> for i in mygenerator:...     print(i)014 

这是一个无用的示例,然而当须要函数返回大量的值(只须要读取一次)时,它就很不便。

把握yield,须要分明的一点是:在调用函数时,在函数主体中编写的代码不会运行,该函数仅返回生成器对象,初学容易对这一点产生困惑。

其次要明确,代码将在每次for应用生成器时从中断处持续。

当初最艰难的局部是:

第一次for调用从您的函数创立的生成器对象时,它将从头开始运行函数中的代码,直到命中为止yield,而后它将返回循环的第一个值。而后,每个后续调用将运行您在函数中编写的循环的下一次迭代,并返回下一个值。这将始终继续到生成器被认为是空的为止,这在函数运行时没有命中时就会产生yield。那可能是因为循环曾经完结,或者是因为您不再满足"if/else"

yield: 出产, 缴出, 让出, 屈从, 让路

用代码来阐明

生成器 generator:

# 创立返回生成器的办法def _get_child_candidates(self, distance, min_dist, max_dist):    # 应用生成器对象一次,上面代码就会被调用一次:    # 如果还有左子对象节点,且间隔适合, 返回下一个子对象    if self._leftchild and distance - max_dist < self._median:        yield self._leftchild    # 如果还有右子对象节点,且间隔适合, 返回下一个子对象    if self._rightchild and distance + max_dist >= self._median:        yield self._rightchild    # 函数执行到这,被认为生成器空了    # 这里不超过两个值了: 左和右子对象 

调用方 caller:

# 创立空的 list, 和一个蕴含以后对象援用的列表result, candidates = list(), [self]# 循环 candidates (开始只有一个元素)while candidates:    # 取最初的 candidate 并从列表中移除    node = candidates.pop()    # 获取 obj 与 candidate 间的间隔    distance = node._get_dist(obj)    # 间隔适合则填入后果    if distance <= max_dist and distance >= min_dist:        result.extend(node._values)    # 并把 candidate 的子元素退出列表    # 循环直至锁定candidate 全副子元素的子元素    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))return result 

此代码蕴含几个智能局部:

  • 对一个列表循环迭代,然而循环在迭代时列表会扩大:-)这是浏览所有这些嵌套数据的一种简洁办法,即便这样做有点危险,因为可能会遇到有限循环。在这种状况下,请candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))耗尽所有生成器的值,然而while持续创立新的生成器对象,因为它们未利用于同一节点,因而将产生与先前值不同的值。
  • extend()办法是冀望可迭代并将其值增加到列表的列表对象办法。

通常咱们将一个列表传递给它:

>>> a = [1, 2]>>> b = [3, 4]>>> a.extend(b)>>> print(a)[1, 2, 3, 4] 

然而在下面的代码中,失去了一个生成器,这更好,因为:

  1. 不须要两次读取值。
  2. 可能有很多子元素,并且不心愿所有子元素都存储在内存中。

这样做之所以无效,是因为 Python 不在乎办法的参数是否为列表。Python 冀望的是可迭代对象,因而它能够与字符串,列表,元组和生成器一起应用!这就是所谓的鸭子输出,这是Python如此酷的起因之一。但这是另一个故事了……

管制生产器耗尽

>>> 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())<type 'exceptions.StopIteration'>>>> wall_street_atm = hsbc.create_atm() # 新的 ATM 也一样>>> print(wall_street_atm.next())<type 'exceptions.StopIteration'>>>> hsbc.crisis = False # 危机过后 ATM 里也没钱>>> print(corner_street_atm.next())<type 'exceptions.StopIteration'>>>> brand_new_atm = hsbc.create_atm() # 搞个新的做新生>>> for cash in brand_new_atm:...    print cash$100$100$100$100$100$100$100$100$100... 

留神:对于Python 3,请应用print(corner_street_atm.__next__())print(next(corner_street_atm))

对于诸如管制对资源的拜访之类的各种事件,这可能很有用。

Itertools,您最好的敌人

itertools模块蕴含用于操纵可迭代对象的非凡性能。是否曾想复制发电机?连锁两个发电机?用一个班轮将值嵌套在嵌套列表中?Map / Zip没有创立另一个列表?

而后就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)] 

理解迭代的外部机制

迭代是一个隐含可迭代对象(实现__iter__()办法)和迭代器(实现__next__()办法)的过程。可迭代对象是能够从中获取迭代器的任何对象。迭代器是使您能够迭代可迭代对象的对象。