成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Python 迭代器、生成器和列表解析

dreamGong / 3510人閱讀

摘要:迭代器迭代器在版本中被加入它為類(lèi)序列對(duì)象提供了一個(gè)類(lèi)序列的接口。其中方法返回迭代器對(duì)象本身方法返回容器的下一個(gè)元素,在結(jié)尾時(shí)引發(fā)異常。迭代器協(xié)議迭代器協(xié)議即實(shí)現(xiàn)與方法。

迭代器

迭代器在 Python 2.2 版本中被加入, 它為類(lèi)序列對(duì)象提供了一個(gè)類(lèi)序列的接口。 Python 的迭代無(wú)縫地支持序列對(duì)象, 而且它還允許迭代非序列類(lèi)型, 包括用戶定義的對(duì)象。即迭代器可以迭代不是序列但表現(xiàn)出序列行為的對(duì)象, 例如字典的 key , 一個(gè)文件的行, 等等。迭代器有以下特性:

提供了可擴(kuò)展的迭代器接口.

對(duì)列表迭代帶來(lái)了性能上的增強(qiáng).

在字典迭代中性能提升.

創(chuàng)建真正的迭代接口, 而不是原來(lái)的隨機(jī)對(duì)象訪問(wèn).

與所有已經(jīng)存在的用戶定義的類(lèi)以及擴(kuò)展的模擬序列和映射的對(duì)象向后兼容

迭代非序列集合(例如映射和文件)時(shí), 可以創(chuàng)建更簡(jiǎn)潔可讀的代碼.

迭代器對(duì)象即實(shí)現(xiàn)了迭代器協(xié)議的對(duì)象,在 Python 中,支持迭代器協(xié)議就是實(shí)現(xiàn)對(duì)象的 __iter__()next() 方法(注:在 Python3 中被改為 next 方法)。其中 __iter__() 方法返回迭代器對(duì)象本身;next() 方法返回容器的下一個(gè)元素,在結(jié)尾時(shí)引發(fā) StopIteration 異常。

迭代器協(xié)議

迭代器協(xié)議即實(shí)現(xiàn) __iter__()next() 方法。這兩個(gè)方法是迭代器最基本的方法,一個(gè)用來(lái)獲得迭代器對(duì)象,一個(gè)用來(lái)獲取容器中的下一個(gè)元素。對(duì)于可迭代對(duì)象,可以使用內(nèi)建函數(shù) iter() 來(lái)獲取它的迭代器對(duì)象:

li = [1, 2]
it = iter(li)
print it

print it.next()
print it.next()
print it.next()

結(jié)果如下所示:


1
2
Traceback (most recent call last):
  File "iter.py", line 21, in 
    print it.next()
StopIteration

列表(list)本身是可迭代的,通過(guò) iter() 方法可以獲得其迭代器對(duì)象,然后就可以通過(guò) next() 方法來(lái)訪問(wèn) list 中的元素。當(dāng)容器中沒(méi)有可以訪問(wèn)的元素時(shí), next() 方法將會(huì)拋出一個(gè) StopIteration 的異常,從而終止迭代器。當(dāng)我們使用 for 語(yǔ)句的時(shí)候,for 語(yǔ)句就會(huì)自動(dòng)的通過(guò) __iter__() 方法來(lái)獲得迭代器對(duì)象,并且通過(guò) next() 方法來(lái)獲取下一個(gè)元素,遇到 StopIteration 異常時(shí)會(huì)自動(dòng)結(jié)束迭代。

自定義迭代器

自己創(chuàng)建迭代器實(shí)際上就是實(shí)現(xiàn)一個(gè)帶有 __iter__() 方法和 next() 方法的類(lèi),用該類(lèi)創(chuàng)建的實(shí)例即是可迭代對(duì)象。例如我們用迭代器來(lái)實(shí)現(xiàn)斐波那契數(shù)列:

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1

    def next(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

    def __iter__(self):
        return self

fibs = Fibs()  // 這將得到一個(gè)無(wú)窮的數(shù)列
for f in fibs:
    if f > 1000:
        print f
        break
    else:
        print f

這里有一個(gè)問(wèn)題,大多數(shù)的序列或者類(lèi)序列都不是無(wú)窮的,所以在達(dá)到一定條件后就該終止。因此我們需要在序列或者類(lèi)序列需要結(jié)束時(shí)引發(fā) StopIteration 異常:

class MyRange(object):
    def __init__(self, n):
        self.idx = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()

myRange = MyRange(3)
for i in myRange:
        print i
可迭代對(duì)象和迭代器對(duì)象

可迭代對(duì)象即具有 __iter__() 方法的對(duì)象,該方法可獲取其迭代器對(duì)象。迭代器對(duì)象即具有 next() 方法的對(duì)象。也就是說(shuō),一個(gè)實(shí)現(xiàn)了 __iter_() 的對(duì)象是可迭代的,一個(gè)實(shí)現(xiàn)了 next() 方法的對(duì)象則是迭代器??傻鷮?duì)象也可以是迭代器對(duì)象,如文件對(duì)象。此時(shí)可迭代對(duì)象自己有 next() 方法,而其 __iter() 方法返回的就是它自己。對(duì)于許多內(nèi)置對(duì)象及其派生對(duì)象,如 list、dict 等,由于需要支持多次打開(kāi)迭代器,因此自己并非迭代器對(duì)象,需要用 __iter_() 方法返回其迭代器對(duì)象,并用迭代器對(duì)象來(lái)訪問(wèn)其它元素。

以上例子中的 myRange 這個(gè)對(duì)象就是一個(gè)可迭代對(duì)象,同時(shí)它本身也是一個(gè)迭代器對(duì)象。對(duì)于一個(gè)可迭代對(duì)象,如果它本身又是一個(gè)迭代器對(duì)象,就會(huì)有這樣一個(gè)問(wèn)題,其沒(méi)有辦法支持多次迭代。如下所示:

myRange = MyRange(3)
print myRange is iter(myRange)

print [i for i in myRange]
print [i for i in myRange]

運(yùn)行結(jié)果:

True
[0, 1, 2]
[]

為了解決上面的問(wèn)題,可以分別定義可迭代類(lèi)型對(duì)象和迭代器類(lèi)型對(duì)象;然后可迭代類(lèi)型對(duì)象的 __iter__() 方法可以獲得一個(gè)迭代器類(lèi)型的對(duì)象。如下所示:

class Zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return ZrangeIterator(self.n)

class ZrangeIterator:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()    

zrange = Zrange(3)
print zrange is iter(zrange)         

print [i for i in zrange]
print [i for i in zrange]

運(yùn)行結(jié)果:

False
[0, 1, 2]
[0, 1, 2]

另外, reversed() 內(nèi)建函數(shù)將返回一個(gè)反序訪問(wèn)的迭代器,enumerate() 內(nèi)建函數(shù)同樣也返回迭代器。例如可以用 enumerate() 函數(shù)遍歷列表:

ll = [1, 2, 3]
print enumerate(ll)

/* 優(yōu)雅的遍歷列表 */
for i, ele in enumerate(ll):
    print i, ll[i]
生成器

迭代器和生成器可能是近幾年引入的最強(qiáng)大的兩個(gè)特性。生成器是一種用普通的函數(shù)語(yǔ)法定義的迭代器,也就是說(shuō)生成器實(shí)際上就是一個(gè)函數(shù)。但是生成器不用 return 返回,而是用 yield 一次返回一個(gè)結(jié)果,在每個(gè)結(jié)果之間掛起和繼續(xù)它們的狀態(tài),來(lái)自動(dòng)實(shí)現(xiàn)迭代協(xié)議。任何包含 yield 語(yǔ)句的函數(shù)稱為生成器。yield 被人們優(yōu)雅的稱之為語(yǔ)法糖,意思就是包在里邊的甜心。在 yield 的內(nèi)部是一個(gè)狀態(tài)機(jī),維護(hù)著掛起和繼續(xù)的狀態(tài)。

生成器執(zhí)行流程

先看看下邊的列子:

def Zrange(n):
    print "beginning of Zrange"
    i = 0
    while i < n:
        print "before yield", i
        yield i
        i += 1
        print "after yield", i

    print "endding of Zrange"

zrange = Zrange(3)
print "------------"

print zrange.next()
print "------------"

print zrange.next()
print "------------"

print zrange.next()
print "------------"

print zrange.next()def flatten(nested):
    result = []
    try:
        # 不要迭代類(lèi)似字符串的對(duì)象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)
    except TypeError:
        result.append(nested)
    return result
print "------------"

執(zhí)行結(jié)果:

--------------------
beginning of Zrange
before yield 0
0
--------------------
after yield 1
before yield 1
1
--------------------
after yield 2
before yield 2
2
--------------------
after yield 3
endding of Zrange
Traceback (most recent call last):
  File "one.py", line 38, in 
    print zrange.next()
StopIteration

通過(guò)結(jié)果可以看到:

當(dāng)調(diào)用生成器函數(shù)的時(shí)候,函數(shù)只是返回了一個(gè)生成器對(duì)象,并沒(méi)有 執(zhí)行。

當(dāng)next()方法第一次被調(diào)用的時(shí)候,生成器函數(shù)才開(kāi)始執(zhí)行,執(zhí)行到y(tǒng)ield語(yǔ)句處停止

next()方法的返回值就是yield語(yǔ)句處的參數(shù)(yielded value)

當(dāng)繼續(xù)調(diào)用next()方法的時(shí)候,函數(shù)將接著上一次停止的yield語(yǔ)句處繼續(xù)執(zhí)行,并到下一個(gè)yield處停止;如果后面沒(méi)有yield就拋出StopIteration異常

遞歸生成器

生成器可以向函數(shù)一樣進(jìn)行遞歸使用,下面列舉兩個(gè)示例:

對(duì)一個(gè)序列進(jìn)行全排列:

def permutations(li):
    if len(li) == 0:
        yield li
    else:
        for i in range(len(li)):
            li[0], li[i] = li[i], li[0] #
            for item in permutations(li[1:]):
                yield [li[0]] + item

for item in permutations(range(3)):
    print item

這里的實(shí)現(xiàn)思路是,每次取序列中不一樣的元素放在最前面,直到只有一個(gè)元素時(shí)返回,并將返回結(jié)果往后拼接。

展開(kāi)多層嵌套的列表:

def flatten(nested):
    try:
        # 不要迭代類(lèi)似于字符串的對(duì)象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

print list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8]))

這里需要注意的是,不應(yīng)該在 flatten 函數(shù)中對(duì)類(lèi)似于字符串的對(duì)象進(jìn)行迭代,這樣會(huì)導(dǎo)致無(wú)窮遞歸,因?yàn)橐粋€(gè)字符串的第一個(gè)元素是另一個(gè)長(zhǎng)度為1的字符串,而長(zhǎng)度為一個(gè)字符串的第一個(gè)元素就是字符串本身。

通用生成器

生成器可以人為是由兩部分組成:生成器的函數(shù)和生成器的迭代器。生成器的函數(shù)是用 def 語(yǔ)句定義的,包含 yield 部分,生成器的迭代器是這個(gè)函數(shù)返回的部分。按照一種不是很準(zhǔn)確的說(shuō)法,兩個(gè)實(shí)體經(jīng)常被當(dāng)做一個(gè),合起來(lái)叫做生成器。如下實(shí)例所示:

def simple_generator():
    yield 1

print simple_generator
print simple_generator()

運(yùn)行結(jié)果:


生成器方法

send(value)

外部作用域訪問(wèn)生成器的 send 方法,就像訪問(wèn) next() 方法一樣。next()方法可以恢復(fù)生成器狀態(tài)并繼續(xù)執(zhí)行,其實(shí) send() 是除 next() 外另一個(gè)恢復(fù)生成器的方法。Python 2.5 中,yield 語(yǔ)句變成了 yield 表達(dá)式,也就是說(shuō) yield 可以有一個(gè)值,而這個(gè)值就是send()方法的參數(shù),所以 send(None) 和 next() 是等效的。同樣,next()和send()的返回值都是 yield語(yǔ) 句處的參數(shù)(yielded value)。使用 send() 方法只有在生成器掛起之后才有意義,如果真想對(duì)剛剛啟動(dòng)的生成器使用 send 方法,則可以將 None 作為參數(shù)進(jìn)行調(diào)用。也就是說(shuō), 第一次調(diào)用時(shí),要使用 next() 語(yǔ)句或 send(None),因?yàn)闆](méi)有 yield 語(yǔ)句來(lái)接收這個(gè)值。

throw()

用于在生成器內(nèi)引發(fā)一個(gè)異常。

close()

用于停止生成器,調(diào)用它時(shí),會(huì)在 yield 運(yùn)行出引發(fā)一個(gè) GeneratorExit 異常。

使用示例:

def Zrange(n):
    i = 0
    while i < n:
        val = yield i
        print "val is", val
        i += 1

zrange = Zrange(5)

print zrange.next()
print zrange.next()
print zrange.send("hello")
print zrange.next()
#print zrange.next()

zrange.close()

print zrange.send("world")
模擬生成器

在舊的 Python 版本中并不支持生成器,那么我們可以用普通的函數(shù)來(lái)模擬生成器。如下所示:

def flatten(nested):
    result = []
    try:
        # 不要迭代類(lèi)似字符串的對(duì)象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)
    except TypeError:
        result.append(nested)
    return result

盡管這個(gè)版本可能不適用于所有的生成器,但對(duì)大多數(shù)生成器來(lái)說(shuō)是可行的。比如,它不適用于一個(gè)無(wú)限的生成器。

列表解析和生成器表達(dá)式 列表解析

列表解析( List comprehensions, 或縮略為 list comps ) 來(lái)自函數(shù)式編程語(yǔ)言 Haskell . 它是一個(gè)非常有用, 簡(jiǎn)單, 而且靈活的工具, 可以用來(lái)動(dòng)態(tài)地創(chuàng)建列表。其語(yǔ)法結(jié)構(gòu)為:

[expr for iter_var in iterable]

這個(gè)語(yǔ)句的核心是 for 循環(huán), 它迭代 iterable 對(duì)象的所有條目. 前邊的 expr 應(yīng)用于序列的每個(gè)成員, 最后的結(jié)果值是該表達(dá)式產(chǎn)生的列表。 迭代變量并不需要是表達(dá)式的一部分。例如用 lambda 函數(shù)計(jì)算序列成員的平方的表達(dá)是為:

map(lambda x: x ** 2, range(6))

這可以用列表解析來(lái)改寫(xiě):

[x ** 2 for x in range(6)]

列表解析的表達(dá)式可以取代內(nèi)建的 map() 函數(shù)以及 lambda , 而且效率更高。結(jié)合 if 語(yǔ)句,列表解析還提供了一個(gè)擴(kuò)展版本的語(yǔ)法:

[expr for iter_var in iterable if cond_expr]

這個(gè)語(yǔ)法在迭代時(shí)會(huì)過(guò)濾/捕獲滿足條件表達(dá)式 cond_expr 的序列成員。例如挑選出序列中的奇數(shù)可以用下邊的方法:

[x for x in seq if x % 2]

列表解析還有很多巧妙的應(yīng)用:

迭代一個(gè)有三行五列的矩陣:

[(x+1,y+1) for x in range(3) for y in range(5)]

計(jì)算出所有非空白字符的數(shù)目:

f = open("hhga.txt", "r")

len([word for line in f for word in line.split()])

生成器表達(dá)式

生成器表達(dá)式是列表解析的一個(gè)擴(kuò)展。列表解析的一個(gè)不足就是必要生成所有的數(shù)據(jù), 用以創(chuàng)建整個(gè)列表。這可能對(duì)有大量數(shù)據(jù)的迭代器有負(fù)面效應(yīng)。生成器表達(dá)式通過(guò)結(jié)合列表解析和生成器解決了這個(gè)問(wèn)題。生成器表達(dá)式在 Python 2.4 被引入, 它與列表解析非常相似,而且它們的基本語(yǔ)法基本相同; 不過(guò)它并不真正創(chuàng)建數(shù)字列表, 而是返回一個(gè)生成器,這個(gè)生成器在每次計(jì)算出一個(gè)條目后,把這個(gè)條目“產(chǎn)生”(yield)出來(lái)。生成器表達(dá)式使用了"延遲計(jì)算"(lazy evaluation), 所以它在使用內(nèi)存上更有效。生成器表達(dá)式語(yǔ)法:

(expr for iter_var in iterable if cond_expr)

生成器并不會(huì)讓列表解析廢棄, 它只是一個(gè)內(nèi)存使用更友好的結(jié)構(gòu), 基于此, 有很多使用生 成器地方,如下所示:

快速地計(jì)算文件大小:

上面我們用列表解析計(jì)算出了文件中非空白字符的數(shù)目,那么只要用 sum() 函數(shù)對(duì)每個(gè)單詞的長(zhǎng)度求和,則可大致計(jì)算出文件的大小。sum() 函數(shù)的參數(shù)不僅可以是列表,還可以是可迭代對(duì)象,比如生成器表達(dá)式。這里我們用生成器表達(dá)式改寫(xiě)整個(gè)過(guò)程:

sum(len(word) for line in data for word in line.split())

交叉配對(duì):

生成器表達(dá)式就好像是懶惰的列表解析(這反而成了它主要的優(yōu)勢(shì))。它還可以用來(lái)處理其他列表或生成器:

rows = [1, 2, 3, 17]

def cols(): # example of simple generator
    yield 56  
    yield 2  
    yield 1

x_product_pairs = ((i, j) for i in rows for j in cols())

for pair in x_product_pairs:
    print pair

尋找文件最長(zhǎng)的行:

def longest(filename):
    glines = (x.strip() for x in open(filename))
    return max([len(l) for l in glines])

# Script starts from here

if __name__ == "__main__":
    print longest("/etc/hosts")

這個(gè)例子摘自 《Python核心編程》 中生成器表達(dá)式一節(jié),作者在原書(shū)中只用了一行代碼來(lái)實(shí)現(xiàn)這個(gè)功能,即:

return max(len(x.strip()) for x in open("/etc/motd"))

這行代碼會(huì)報(bào)出如下錯(cuò)誤:

TypeError: object of type "generator" has no len()

也就是說(shuō)生成器沒(méi)有 len() 方法,所以這樣并不可行,但是用列表解析則可以用一行實(shí)現(xiàn):

return max([len(x.strip()) for x in open("/etc/motd")])

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/43095.html

相關(guān)文章

  • Python迭代、生成、裝飾深入解讀

    摘要:前言首先,明確可迭代對(duì)象迭代器和生成器這三個(gè)概念。迭代器對(duì)象傳送門(mén)之迭代器實(shí)現(xiàn)原理首先明確它是一個(gè)帶狀態(tài)的對(duì)象。生成器是一種特殊的迭代器,它的返回值不是通過(guò)而是用。 前言 首先,明確可迭代對(duì)象、迭代器和生成器這三個(gè)概念。 可迭代對(duì)象(Iterable) 可迭代對(duì)象(Iterable Object),簡(jiǎn)單的來(lái)理解就是可以使用 for 來(lái)循環(huán)遍歷的對(duì)象。比如常見(jiàn)的 list、set和di...

    codercao 評(píng)論0 收藏0
  • python開(kāi)發(fā)第五篇--裝飾內(nèi)置函數(shù)

    摘要:裝飾器的應(yīng)用場(chǎng)景比如插入日志,性能測(cè)試,事務(wù)處理,緩存等等場(chǎng)景。裝飾器完美的遵循了這個(gè)開(kāi)放封閉原則。迭代器迭代器遵循迭代器協(xié)議必須擁有方法和方法。直到函數(shù)執(zhí)行結(jié)束。調(diào)用相關(guān)函數(shù)用于檢查一個(gè)對(duì)象是否是可調(diào)用的。 裝飾器 裝飾器的含義: 1.裝飾器本質(zhì)上就是一個(gè)python函數(shù),他可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下,增加額外的功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。2.裝飾器的應(yīng)用...

    Integ 評(píng)論0 收藏0
  • [譯] PEP 255--簡(jiǎn)單的生成

    摘要:第四種選擇是在不同的線程中運(yùn)行生產(chǎn)者和消費(fèi)者。包含語(yǔ)句的函數(shù)被稱為生成器函數(shù)。然后引發(fā)一個(gè)異常,表明迭代器已經(jīng)耗盡。換句話說(shuō),未捕獲的異常終結(jié)了生成器的使用壽命。 showImg(https://segmentfault.com/img/bVbntUq?w=4272&h=2848);我正打算寫(xiě)寫(xiě) Python 的生成器,然而查資料時(shí)發(fā)現(xiàn),引入生成器的 PEP 沒(méi)人翻譯過(guò),因此就花了點(diǎn)時(shí)...

    tracymac7 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<