Python迭代器和生成器

· 迭代器

什麼是迭代器:
迭代器可以讓我們訪問集合的時候變得非常方便。之前我們通過for…in…來訪問一個集合的時候,就是使用迭代器完成的。
如果沒有迭代器,那麼我們只能通過while循環,每次循環的時候通過下標來訪問了。
可迭代對象:
可以直接使用for循環遍歷的對象,成爲可迭代的對象,常見的可迭代對象有:list、tuple、dict、set、str以及生成器
更加專業的判斷一個對象是否是可迭代對象:這個對象有一個__iter__方法,並且這個方法會返回一個迭代器對象,這種對象就叫做可迭代對象。
判斷一個對象是否可迭代:
可以使用is instance()判斷一個對象是否是interable對象

from collections import Iterable

# 列表是一個可迭代對象
ret = isinstance([1, 2, 3], Iterable)
print(ret)

字符串是一個可迭代對象

ret = isinstance('abc', Iterable)
print(ret)

整形不是一個可迭代對象

ret = isinstance(123, Iterable)
print(ret)

迭代器:
1、在python2中,實現了next和__iter__方法,並且在這個方法中返回了值的對象,叫做迭代器對象
2、在python3中,實現了__next__方法和__iter__方法,並且在這個方法中返回了值的對象,叫做迭代器對象
3、如果迭代器沒有返回值了,那麼應該在next或者是__next__方法中拋出一個StopInteration
使用iter()方法獲取可迭代對象的迭代器:
有時候我們擁有了一個可迭代的對象,我們想要獲取這個迭代器,那麼可以通過iter(x)方法獲取這個可迭代對象
的迭代器。
自定義迭代器:

分開的寫法(方法A):

class MyRangeIterator:
    """
    迭代器
    """
    def __init__(self, start, end):
        self.index = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < self.end:
            temp = self.index
            self.index += 1
            return temp
        else:
            raise StopIteration()


class MyRange:
    """
    MyRange是可迭代對象
    """
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        # 這個方法中返回一個迭代器對象
        return MyRangeIterator(self.start, self.end)


if __name__ == '__main__':
    myrange = MyRange(1, 10)
    for i in myrange:
        print(i)

    iterator = iter(myrange)
    while True:
        try:
            x = iterator.__next__()
            print(x)
        except StopIteration:
            break

合併到一起的寫法(方法B):

# -*- coding: UTF-8 -*-

from collections import Iterable, Iterator

# Interable: 判斷是否可迭代
# Interator: 判斷是否是迭代器

print(isinstance('abc', Iterable))
print(isinstance('abc', Iterator))

class MyRange:

    def __init__(self, start, end):
        self.index = start
        self.end = end

    def __iter__(self):
        # 這個方法要返回一個迭代器
        return self

    def __next__(self):
        if self.index < self.end:
            temp = self.index
            self.index += 1
            return temp
        else:
            raise StopIteration()

if __name__ == '__main__':
    myrange = MyRange(1, 10)
    for i in myrange:
        print(i)
    for i in myrange:
        print(i)

以上兩個方法,A和B都實現了迭代器的功能,當我們實例化MyRange時,即獲得了一個可迭代對象,在方法A中這個可迭代對象返回了一個迭代器:MyRangeIterator(self.start, self.end),方法B中返回了自己self本身。這時就會有一個區別產生,因爲A方法在每次返回迭代器的時候,都會實例化迭代
器,而B中只會實例化一次,因此:

myrange = MyRange(1, 10)
for i in myrange:
    print(i)
for i in myrange:
    print(i)

如果兩個方法都執行上述代碼,A方法每次都會遍歷出1到9,而B方法只會遍歷一次,第二次什麼都不會打印。因爲第二次self.index 已經等於了 self.end。

· 生成器
爲什麼需要生成器:
當我們需要打印一個1到1億的整形的時候,如果我們採用普通的方式,直接調用range函數,程序會崩潰掉。因爲range(1,100000000)函數會直接產生一個從1-1億的列表,這個列表中所有數據都是存放在內存中的,會導致內存爆滿。這時候我們可以採用生成器來解決該問題,生成器不會一次把所有數據都加載到內存中,而是在循環的時候臨時產生的,循環一次生成一個,所以程序在運行期間永遠都只會生成一個數據,從而節省內存的開銷。
next函數和__next__方法:
next函數可以迭代生成器的返回值
自己寫生成器:
生成器可以通過函數產生。如果在一個函數中出現了yield表達式,那麼這個函數將不再是一個普通的函數,而是一個生成器函數。yield一次返回一個結果,並且會凍結當前函數的狀態。

普通的列表

num_list = [x for x in range(1, 100)]
print(num_list)

生成式

num_gen = (x for x in range(1, 100))
print(num_gen)
print(type(num_gen))

for i in num_gen:

print(i)

def my_gen():
yield 1
yield 2
yield 3

ret = my_gen()
print(next(ret))
print(next(ret))
print(next(ret))

print(next(ret))

def gen(start, end):
index = start
while index <= end:
yield index
index += 1

生成器有兩個身份:迭代器和可迭代的對象, 因此,只能被遍歷一次

ret = gen(1, 100000000)
for i in ret:
print(i)
send方法:
1、也是用來觸發代碼,直接碰到yield表達式
2、如果用send方法執行剛剛開始的生成器,那麼應該傳遞None給send方法
def my_gen(start):
while start < 10:
# 如果通過next函數執行yield
# 那麼yield xxx 永遠都是返回None
temp = yield start
print(temp)
start += 1

ret = my_gen(1)

第一次使用send方法必須傳None

print(ret.send(None))
print(next(ret))
print(ret.send(‘hello’))

1

None

2

hello

3

send方法和next函數的區別:
1、send方法可以傳遞值給yield表達式,而next不可以
2、在第一次執行生成器代碼的時候,必須要傳None,next則不需要
生成器的一些案例:

def fib(count):
    index = 1
    a, b = 0, 1
    while index <= count:
        yield b
        c = b
        b = a + b
        a = c
        index +=1
for x in fib(7):
    print(x)

#使用生成器 來實現多線程,聽音樂和看電影同時進行
def netease_music(duration):
    time = 0
    while time <= duration:
        print('音樂聽了%d分鐘' % time)
        time += 1
        yield None
    # raise StopIteration()

def youku_movie(duration):
    time = 0
    while time <= duration:
        print('電影看了%d分鐘' % time)
        time += 1
        yield None
    # raise StopIteration()


if __name__ == '__main__':
    music_iter = netease_music(10)
    movie_iter = youku_movie(30)
    music_stop = False
    movie_stop = False
    while True:
        try:
            next(music_iter)
        except StopIteration:
            print('音樂聽完了')
            music_stop = True
        try:
            next(movie_iter)
        except StopIteration:
            print('電影看完了')
            movie_stop = True
        if music_stop and movie_stop:
            break
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章