最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Python的迭代器、生成器以及相關(guān)的itertools包

 更新時(shí)間:2015年04月02日 10:31:50   作者:Sahand Saba  
這篇文章主要介紹了詳解Python的迭代器、生成器以及相關(guān)的itertools包,Iterators、Generators是Python的高級(jí)特性,亦是Python學(xué)習(xí)當(dāng)中必會(huì)的基本知識(shí),需要的朋友可以參考下

對(duì)數(shù)學(xué)家來說,Python這門語言有著很多吸引他們的地方。舉幾個(gè)例子:對(duì)于tuple、lists以及sets等容器的支持,使用與傳統(tǒng)數(shù)學(xué)類似的符號(hào)標(biāo)記方式,還有列表推導(dǎo)式這樣與數(shù)學(xué)中集合推導(dǎo)式和集的結(jié)構(gòu)式(set-builder notation)很相似的語法結(jié)構(gòu)。

另外一些很吸引數(shù)學(xué)愛好者的特性是Python中的iterator(迭代器)、generator(生成器)以及相關(guān)的itertools包。這些工具幫助人們能夠很輕松的寫出處理諸如無窮序列(infinite sequence)、隨機(jī)過程(stochastic processes)、遞推關(guān)系(recurrence relations)以及組合結(jié)構(gòu)(combinatorial structures)等數(shù)學(xué)對(duì)象的優(yōu)雅代碼。本文將涵蓋我關(guān)于迭代器和生成器的一些筆記,并且有一些我在學(xué)習(xí)過程中積累的相關(guān)經(jīng)驗(yàn)。
Iterators

迭代器(Iterator)是一個(gè)可以對(duì)集合進(jìn)行迭代訪問的對(duì)象。通過這種方式不需要將集合全部載入內(nèi)存中,也正因如此,這種集合元素幾乎可以是無限的。你可以在Python官方文檔的“迭代器類型(Iterator Type)”部分找到相關(guān)文檔。

讓我們對(duì)定義的描述再準(zhǔn)確些,如果一個(gè)對(duì)象定義了__iter__方法,并且此方法需要返回一個(gè)迭代器,那么這個(gè)對(duì)象就是可迭代的(iterable)。而迭代器是指實(shí)現(xiàn)了__iter__以及next(在Python 3中為__next__)兩個(gè)方法的對(duì)象,前者返回一個(gè)迭代器對(duì)象,而后者返回迭代過程的下一個(gè)集合元素。據(jù)我所知,迭代器總是在__iter__方法中簡單的返回自己(self),因?yàn)樗鼈冋亲约旱牡鳌?/p>

一般來說,你應(yīng)該避免直接調(diào)用__iter__以及next方法。而應(yīng)該使用for或是列表推導(dǎo)式(list comprehension),這樣的話Python能夠自動(dòng)為你調(diào)用這兩個(gè)方法。如果你需要手動(dòng)調(diào)用它們,請(qǐng)使用Python的內(nèi)建函數(shù)iter以及next,并且把目標(biāo)迭代器對(duì)象或是集合對(duì)象當(dāng)做參數(shù)傳遞給它們。舉個(gè)例子,如果c是一個(gè)可迭代對(duì)象,那么你可以使用iter(c)來訪問,而不是c.__iter__(),類似的,如果a是一個(gè)迭代器對(duì)象,那么請(qǐng)使用next(a)而不是a.next()來訪問下一個(gè)元素。與之相類似的還有l(wèi)en的用法。

說到len,值得注意的是對(duì)迭代器而言沒必要去糾結(jié)length的定義。所以它們通常不會(huì)去實(shí)現(xiàn)__len__方法。如果你需要計(jì)算容器的長度,那么必須得手動(dòng)計(jì)算,或者使用sum。本文末,在itertools模塊之后會(huì)給出一個(gè)例子。

有一些可迭代對(duì)象并不是迭代器,而是使用其他對(duì)象作為迭代器。舉個(gè)例子,list對(duì)象是一個(gè)可迭代對(duì)象,但并不是一個(gè)迭代器(它實(shí)現(xiàn)了__iter__但并未實(shí)現(xiàn)next)。通過下面的例子你可以看到list是如何使用迭代器listiterator的。同時(shí)值得注意的是list很好地定義了length屬性,而listiterator卻沒有。

>>> a = [1, 2]
>>> type(a)
<type 'list'>
>>> type(iter(a))
<type 'listiterator'>
>>> it = iter(a)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration
>>> len(a)
2
>>> len(it)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: object of type 'listiterator' has no len()

當(dāng)?shù)Y(jié)束卻仍然被繼續(xù)迭代訪問時(shí),Python解釋器會(huì)拋出StopIteration異常。然而,前述中提到迭代器可以迭代一個(gè)無窮集合,所以對(duì)于這種迭代器就必須由用戶負(fù)責(zé)確保不會(huì)造成無限循環(huán)的情況,請(qǐng)看下面的例子:
 

class count_iterator(object):
  n = 0
 
  def __iter__(self):
    return self
 
  def next(self):
    y = self.n
    self.n += 1
    return y

下面是例子,注意最后一行試圖將一個(gè)迭代器對(duì)象轉(zhuǎn)為list,這將導(dǎo)致一個(gè)無限循環(huán),因?yàn)檫@種迭代器對(duì)象將不會(huì)停止。
 

>>> counter = count_iterator()
>>> next(counter)
0
>>> next(counter)
1
>>> next(counter)
2
>>> next(counter)
3
>>> list(counter) # This will result in an infinite loop!

最后,我們將修改以上的程序:如果一個(gè)對(duì)象沒有__iter__方法但定義了__getitem__方法,那么這個(gè)對(duì)象仍然是可迭代的。在這種情況下,當(dāng)Python的內(nèi)建函數(shù)iter將會(huì)返回一個(gè)對(duì)應(yīng)此對(duì)象的迭代器類型,并使用__getitem__方法遍歷list的所有元素。如果StopIteration或IndexError異常被拋出,則迭代停止。讓我們看看以下的例子:
 

class SimpleList(object):
  def __init__(self, *items):
    self.items = items
 
  def __getitem__(self, i):
    return self.items[i]

用法在此:
 

>>> a = SimpleList(1, 2, 3)
>>> it = iter(a)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

現(xiàn)在來看一個(gè)更有趣的例子:根據(jù)初始條件使用迭代器生成Hofstadter Q序列。Hofstadter在他的著作《G?del, Escher, Bach: An Eternal Golden Braid》中首次提到了這個(gè)嵌套的序列,并且自那時(shí)候開始關(guān)于證明這個(gè)序列對(duì)所有n都成立的問題就開始了。以下的代碼使用一個(gè)迭代器來生成給定n的Hofstadter序列,定義如下:

Q(n)=Q(n-Q(n-1))+Q(n?Q(n?2))

給定一個(gè)初始條件,舉個(gè)例子,qsequence([1, 1])將會(huì)生成H序列。我們使用StopIteration異常來指示序列不能夠繼續(xù)生成了,因?yàn)樾枰粋€(gè)合法的下標(biāo)索引來生成下一個(gè)元素。例如如果初始條件是[1,2],那么序列生成將立即停止。
 

class qsequence(object):
  def __init__(self, s):
    self.s = s[:]
 
  def next(self):
    try:
      q = self.s[-self.s[-1]] + self.s[-self.s[-2]]
      self.s.append(q)
      return q
    except IndexError:
      raise StopIteration()
 
  def __iter__(self):
    return self
 
  def current_state(self):
    return self.s

用法在此:
 

>>> Q = qsequence([1, 1])
>>> next(Q)
2
>>> next(Q)
3
>>> [next(Q) for __ in xrange(10)]
[3, 4, 5, 5, 6, 6, 6, 8, 8, 8]
Generators

生成器(Generator)是一種用更簡單的函數(shù)表達(dá)式定義的生成器。說的更具體一些,在生成器內(nèi)部會(huì)用到y(tǒng)ield表達(dá)式。生成器不會(huì)使用return返回值,而當(dāng)需要時(shí)使用yield表達(dá)式返回結(jié)果。Python的內(nèi)在機(jī)制能夠幫助記住當(dāng)前生成器的上下文,也就是當(dāng)前的控制流和局部變量的值等。每次生成器被調(diào)用都適用yield返回迭代過程中的下一個(gè)值。__iter__方法是默認(rèn)實(shí)現(xiàn)的,意味著任何能夠使用迭代器的地方都能夠使用生成器。下面這個(gè)例子實(shí)現(xiàn)的功能同上面迭代器的例子一樣,不過代碼更緊湊,可讀性更強(qiáng)。
 

def count_generator():
  n = 0
  while True:
   yield n
   n += 1

來看看用法:

>>> counter = count_generator()
>>> counter
<generator object count_generator at 0x106bf1aa0>
>>> next(counter)
0
>>> next(counter)
1
>>> iter(counter)
<generator object count_generator at 0x106bf1aa0>
>>> iter(counter) is counter
True
>>> type(counter)
<type 'generator'>

現(xiàn)在讓我們嘗試用生成器來實(shí)現(xiàn)Hofstadter's Q隊(duì)列。這個(gè)實(shí)現(xiàn)很簡單,不過我們卻不能實(shí)現(xiàn)前的類似于current_state那樣的函數(shù)了。因?yàn)閾?jù)我所知,不可能在外部直接訪問生成器內(nèi)部的變量狀態(tài),因此如current_state這樣的函數(shù)就不可能實(shí)現(xiàn)了(雖然有諸如gi_frame.f_locals這樣的數(shù)據(jù)結(jié)構(gòu)可以做到,但是這畢竟是CPython的特殊實(shí)現(xiàn),并不是這門語言的標(biāo)準(zhǔn)部分,所以并不推薦使用)。如果需要訪問內(nèi)部變量,一個(gè)可能的方法是通過yield返回所有的結(jié)果,我會(huì)把這個(gè)問題留作練習(xí)。
 

def hofstadter_generator(s):
  a = s[:]
  while True:
    try:
      q = a[-a[-1]] + a[-a[-2]]
      a.append(q)
      yield q
    except IndexError:
      return

請(qǐng)注意,在生成器迭代過程的結(jié)尾有一個(gè)簡單的return語句,但并沒有返回任何數(shù)據(jù)。從內(nèi)部來說,這將拋出一個(gè)StopIteration異常。

下一個(gè)例子來自Groupon的面試題。在這里我們首先使用兩個(gè)生成器來實(shí)現(xiàn)Bernoulli過程,這個(gè)過程是一個(gè)隨機(jī)布爾值的無限序列,True的概率是p而False的概率為q=1-p。隨后實(shí)現(xiàn)一個(gè)von Neumann extractor,它從Bernoulli process獲取輸入(0<p<1),并且返回另一個(gè)Bernoulli process(p=0.5)。
 

import random
 
def bernoulli_process(p):
  if p > 1.0 or p < 0.0:
    raise ValueError("p should be between 0.0 and 1.0.")
 
  while True:
    yield random.random() < p
 
def von_neumann_extractor(process):
  while True:
    x, y = process.next(), process.next()
    if x != y:
      yield x

最后,生成器是一種生成隨機(jī)動(dòng)態(tài)系統(tǒng)的很有利的工具。下面這個(gè)例子將演示著名的帳篷映射(tent map)動(dòng)態(tài)系統(tǒng)是如何通過生成器實(shí)現(xiàn)的。(插句題外話,看看數(shù)值的不準(zhǔn)確性是如何開始關(guān)聯(lián)變化并呈指數(shù)式增長的,這是一個(gè)如帳篷映射這樣的動(dòng)態(tài)系統(tǒng)的關(guān)鍵特征)。

>>> def tent_map(mu, x0):
...  x = x0
...  while True:
...    yield x
...    x = mu * min(x, 1.0 - x)
...
>>>
>>> t = tent_map(2.0, 0.1)
>>> for __ in xrange(30):
...  print t.next()
...
0.1
0.2
0.4
0.8
0.4
0.8
0.4
0.8
0.4
0.8
0.4
0.8
0.4
0.8
0.4
0.8
0.4
0.799999999999
0.400000000001
0.800000000003
0.399999999994
0.799999999988
0.400000000023
0.800000000047
0.399999999907
0.799999999814
0.400000000373
0.800000000745
0.39999999851
0.79999999702

另一個(gè)相似的例子是Collatz序列。
 

def collatz(n):
  yield n
  while n != 1:
   n = n / 2 if n % 2 == 0 else 3 * n + 1
   yield n

請(qǐng)注意在這個(gè)例子中,我們?nèi)耘f沒有手動(dòng)拋出StopIteration異常,因?yàn)樗鼤?huì)在控制流到達(dá)函數(shù)結(jié)尾的時(shí)候自動(dòng)拋出。

請(qǐng)看用法:
 

>>> # If the Collatz conjecture is true then list(collatz(n)) for any n will
... # always terminate (though your machine might run out of memory first!)
>>> list(collatz(7))
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
>>> list(collatz(13))
[13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
>>> list(collatz(17))
[17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
>>> list(collatz(19))
[19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
Recursive Generators

生成器可以像其它函數(shù)那樣遞歸。讓我們來看一個(gè)自實(shí)現(xiàn)的簡單版本的itertools.permutations,這個(gè)生成器通過給定一個(gè)item列表生成其全排列(在實(shí)際中請(qǐng)使用itertools.permutations,那個(gè)實(shí)現(xiàn)更快)?;舅枷牒芎唵危簩?duì)列表中的每一個(gè)元素,我們通過將它與列表第一個(gè)元素交換將其放置到第一的位置上去,而后重新遞歸排列列表的剩余部分。
 

def permutations(items):
  if len(items) == 0:
    yield []
  else:
    pi = items[:]
    for i in xrange(len(pi)):
      pi[0], pi[i] = pi[i], pi[0]
      for p in permutations(pi[1:]):
        yield [pi[0]] + p

 

>>> for p in permutations([1, 2, 3]):
...   print p
...
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
Generator Expressions

生成器表達(dá)式可以讓你通過一個(gè)簡單的,單行聲明定義生成器。這跟Python中的列表推導(dǎo)式非常類似,舉個(gè)例子,下面的代碼將定義一個(gè)生成器迭代所有的完全平方。注意生成器表達(dá)式的返回結(jié)果是一個(gè)生成器類型對(duì)象,它實(shí)現(xiàn)了next和__iter__兩個(gè)方法。
 

>>> g = (x ** 2 for x in itertools.count(1))
>>> g
<generator object <genexpr> at 0x1029a5fa0>
>>> next(g)
1
>>> next(g)
4
>>> iter(g)
<generator object <genexpr> at 0x1029a5fa0>
>>> iter(g) is g
True
>>> [g.next() for __ in xrange(10)]
[9, 16, 25, 36, 49, 64, 81, 100, 121, 144]

同樣可以使用生成器表達(dá)式實(shí)現(xiàn)Bernoulli過程,在這個(gè)例子中p=0.4。如果一個(gè)生成器表達(dá)式需要另一個(gè)迭代器作為循環(huán)指示器,并且這個(gè)生辰器表達(dá)式使用在無限序列上的,那么itertools.count將是一個(gè)很好的選擇。若非如此,xrange將是一個(gè)不錯(cuò)的選擇。
 

>>> g = (random.random() < 0.4 for __ in itertools.count())
>>> [g.next() for __ in xrange(10)]
[False, False, False, True, True, False, True, False, False, True]

正如前面提到的,生成器表達(dá)式能夠用在任何需要迭代器作為參數(shù)的地方。舉個(gè)例子,我們可以通過如下代碼計(jì)算前十個(gè)全平方數(shù)的累加和:
 

>>> sum(x ** 2 for x in xrange(10))
285

更多生成器表達(dá)式的例子將在下一節(jié)給出。
itertools模塊

itertools模塊提供了一系列迭代器能夠幫助用戶輕松地使用排列、組合、笛卡爾積或其他組合結(jié)構(gòu)。

在開始下面的部分之前,注意到上面給出的所有代碼都是未經(jīng)優(yōu)化的,在這里只是充當(dāng)一個(gè)示例的作用。在實(shí)際使用中,你應(yīng)該避免自己去實(shí)現(xiàn)排列組合除非你能夠有更好的想法,因?yàn)槊杜e的數(shù)量可是按照指數(shù)級(jí)增加的。

讓我們先從一些有趣的用例開始。第一個(gè)例子來看如何寫一個(gè)常用的模式:循環(huán)遍歷一個(gè)三維數(shù)組的所有下標(biāo)元素,并且循環(huán)遍歷滿足0≤i<j<k≤n條件的所有下標(biāo)。

from itertools import combinations, product
 
n = 4
d = 3
 
def visit(*indices):
  print indices
 
# Loop through all possible indices of a 3-D array
for i in xrange(n):
  for j in xrange(n):
    for k in xrange(n):
      visit(i, j, k)
 
# Equivalent using itertools.product
for indices in product(*([xrange(n)] * d)):
  visit(*indices)
 
# Now loop through all indices 0 <= i < j < k <= n
for i in xrange(n):
  for j in xrange(i + 1, n):
    for k in xrange(j + 1, n):
      visit(i, j, k)
 
# And equivalent using itertools.combinations
for indices in combinations(xrange(n), d):
  visit(*indices)

使用itertools模塊提供的枚舉器有兩個(gè)好處:代碼能夠在單行內(nèi)完成,并且很容易擴(kuò)展到更高維度。我并未比較for方法和itertools兩種方法的性能,也許跟n有很大關(guān)系。如果你想的話請(qǐng)自行測試評(píng)判。

第二個(gè)例子,來做一些有趣的數(shù)學(xué)題:使用生成器表達(dá)式、itertools.combinations以及itertools.permutations來計(jì)算排列的逆序數(shù),并且計(jì)算一個(gè)列表全排列逆序數(shù)之和。如OEIS A001809所示,求和的結(jié)果趨近于n!n(n-1)/4。在實(shí)際使用中直接通過這公式計(jì)算要比上面的代碼更高效,不過我寫這個(gè)例子是為了練習(xí)itertools枚舉器的使用。
 

import itertools
import math
 
def inversion_number(A):
  """Return the number of inversions in list A."""
  return sum(1 for x, y in itertools.combinations(xrange(len(A)), 2) if A[x] > A[y])
 
def total_inversions(n):
  """Return total number of inversions in permutations of n."""
  return sum(inversion_number(A) for A in itertools.permutations(xrange(n)))

用法如下:
 

>>> [total_inversions(n) for n in xrange(10)]
[0, 0, 1, 9, 72, 600, 5400, 52920, 564480, 6531840]
 
>>> [math.factorial(n) * n * (n - 1) / 4 for n in xrange(10)]
[0, 0, 1, 9, 72, 600, 5400, 52920, 564480, 6531840]

第三個(gè)例子,通過brute-force counting方法計(jì)算recontres number。recontres number的定義在此。首先,我們寫了一個(gè)函數(shù)在一個(gè)求和過程中使用生成器表達(dá)式去計(jì)算排列中fixed points出現(xiàn)的個(gè)數(shù)。然后在求和中使用itertools.permutations和其他生成器表達(dá)式計(jì)算包含n個(gè)數(shù)并且有k個(gè)fixed points的排列的總數(shù)。然后得到結(jié)果。當(dāng)然了,這個(gè)實(shí)現(xiàn)方法是效率低下的,不提倡在實(shí)際應(yīng)用中使用。再次重申,這只是為了掩飾生成器表達(dá)式以及itertools相關(guān)函數(shù)使用方法的示例。
 

def count_fixed_points(p):
  """Return the number of fixed points of p as a permutation."""
  return sum(1 for x in p if p[x] == x)
 
def count_partial_derangements(n, k):
  """Returns the number of permutations of n with k fixed points."""
  return sum(1 for p in itertools.permutations(xrange(n)) if count_fixed_points(p) == k)

用法:
 

# Usage:
>>> [count_partial_derangements(6, i) for i in xrange(7)]
[265, 264, 135, 40, 15, 0, 1]

相關(guān)文章

最新評(píng)論

双辽市| 琼结县| 腾冲县| 石狮市| 新郑市| 淮阳县| 项城市| 苍南县| 仪征市| 元朗区| 白水县| 资源县| 都昌县| 安达市| 日喀则市| 紫阳县| 德庆县| 珲春市| 永丰县| 肃南| 西安市| 胶州市| 克什克腾旗| 临夏县| 广宁县| 且末县| 涡阳县| 越西县| 祁阳县| 凤山市| 青田县| 民和| 凤台县| 盐源县| 馆陶县| 明光市| 镇远县| 唐山市| 缙云县| 自治县| 奎屯市|