python 迭代器,生成器 原理及使用

python 迭代器,生成器

迭代器 iterator

迭代是訪問幾何元素的一種方式. 迭代器是一個可以記住遍歷的位置的對象. 迭代器對象從集合的第一個元素開始訪問, 直到所有的元素被訪問結束, 迭代器只能往前不會後退.

1.可迭代對象

我們已經知道可以對 list, tuple, str等類型的數據使用for…in…的循環語法從其中一次拿到數據進行使用, 我們打這樣的過程稱爲遍歷, 也叫迭代

我們把可以通過for…in…這類語句迭代讀取一條數據供我們使用的對象稱爲可迭代對象(iterable)

###2判斷一個對象是可迭代對象

可以使用 isinstance() 判斷一個對象是否是 iterable 對象:

# –*– coding: utf-8 –*–
# @Time      : 2019/3/27 20:28
# @Author    : Damon_duanlei
# @FileName  : iter_test01.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei

from collections import Iterable

a = list()
b = tuple()
c = dict()
d = "hello world"
e = 618
f = 11.2
print(isinstance(a, Iterable))
print(isinstance(b, Iterable))
print(isinstance(c, Iterable))
print(isinstance(d, Iterable))
print(isinstance(e, Iterable))
print(isinstance(f, Iterable))

運行結果:

True
True
True
True
False
False

3 可迭代對象的本質

可迭代對象的本質就是可以向我們提供一個迭代器幫助我們對其進行迭代遍歷使用. 可迭代對象通過 __iteration__提供一個迭代器, 在迭代一個可迭代對象的時候, 實際上就是先獲取該對象提供的迭代器, 然後通過這個迭代器來以此獲取對象中的每一個數據. 那麼也就是說, 一個具備__iter__方法的對象,就是一個可迭代對象.

如下代碼:

# –*– coding: utf-8 –*–
# @Time      : 2019/3/27 20:43
# @Author    : Damon_duanlei
# @FileName  : iter_test02.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei
from collections import Iterable


class MyList(object):
    def __init__(self):
        self.container = []

    def add(self, item):
        self.container.append(item)

    def __iter__(self):
        pass


if __name__ == '__main__':
    mylist = MyList()
    print(isinstance(mylist, Iterable))

運行結果:

Ture

4. iter()函數與 nest() 函數

通過對可迭代對象使用 iter() 函數獲取此可迭代對象的迭代器. 然後對取到的迭代器不斷使用 next() 函數來獲取下一條數據. iter() 函數實際上就是調用了可迭代對象的__iter__方法.

# –*– coding: utf-8 –*–
# @Time      : 2019/3/27 21:27
# @Author    : Damon_duanlei
# @FileName  : iter_test03.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei
list_a = [11, 22, 33, 44, 55]
iter_list = iter(list_a)
print(next(iter_list))
print("*" * 50)
print(next(iter_list))
print("*" * 50)
print(next(iter_list))

運行結果:

11
**************************************************
22
**************************************************
33

5.判斷一個對象是否是迭代器

from collections import Iterator

a = []
print(isinstance(a, Iterator))
print("*" * 50)
print(isinstance(iter(a), Iterator))

結果:

False
**************************************************
True

6.迭代器

迭代器是用來記錄每次迭代訪問到的位置, 當對迭代器使用 nest() 函數的時候, 迭代器會返回他所記錄位置的下一個位置的數據. 實際上, 在使用 nest() 函數的時候, 調用的 就是迭代器對象的 __nest__方法(python3中是對象的__nest__方法, python2 中是對象的 nest() 方法). python 要求迭代器本身也是可迭代對象, 所以還要爲迭代器對象實現 __iter__方法, 而 __iter__方法要返回一個迭代器, 迭代器本身正是一個迭代器, 所以迭代器的 __iter__方法返回自身即可.

一個實現了 __iter__ 方法和 __nest__方法的對象, 就是迭代器.

# –*– coding: utf-8 –*–
# @Time      : 2019/3/27 21:34
# @Author    : Damon_duanlei
# @FileName  : iter_test04.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei


class MyList(object):
    def __init__(self):
        self.container = []
        self.mark = 0

    def add(self, item):
        self.container.append(item)

    def __iter__(self):
        return self

    def __next__(self):
        if self.mark < len(self.container):
            ret = self.container[self.mark]
            self.mark += 1
            return ret
        else:
            raise StopIteration


if __name__ == '__main__':
    dog_list = MyList()
    dog_list.add("臭臭")
    dog_list.add("小迪")
    dog_list.add("笨笨")

    for dog in dog_list:
        print(dog)

執行結果:

臭臭
小迪
笨笨

7. for 循環的本質

for item in xxx_obj:

  1. 判斷 xxx_obj 是否爲可迭代對象.(是否有 __iter__方法)
  2. 在第一步成立前提下, 系統調用 iter() 函數. 得到 xxx_obj對象 __iter__ 方法的返回值
  3. __iter__方法的返回值是一個迭代器. (有 __iter____nest__方法)
  4. for 不斷的調用迭代器中 __nest__方法並將值賦給 item, 當遇到 Stopiteration 的異常後循環結束.

8.使用場景

迭代器最核心的功能就是可以通過next()函數的調用來返回下⼀個數
據值。如果每次返回的數據值不是在一個已有的數據集合中讀取的,而是通
過程序按照一定的規律計算生成的,那麼也就意味着可以不用再依賴一個已
有的數據集合,也就是說不用再將所有要迭代的數據都一次性緩存下來供後
續依次讀取,這樣可以節省大量的存儲(內存)空間。

如 : python2 中的 xrange

​ python3中的 range

示例: 使用生成器完成斐波那契數列的前 n 項

# –*– coding: utf-8 –*–
# @Time      : 2019/3/27 22:20
# @Author    : Damon_duanlei
# @FileName  : iter_test05.py
# @BlogsAddr : https://blog.csdn.net/Damon_duanlei


class FibIterator(object):
    def __init__(self, num):
        self.count = 0
        self.n = num
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.count < self.n:
            ret = self.a
            self.a, self.b = self.b, self.a + self.b
            self.count += 1
            return ret
        else:
            raise StopIteration


if __name__ == '__main__':
    fib = FibIterator(10)
    for number in fib:
        print(number)

生成器 genenrator

1. 生成器

利用迭代器,可以在每次迭代獲取數據 (通過 nest() 方法) 時按照特定的規律進行生成. 但是, 在實現一個迭代器時, 關於當前迭代到的狀態需要自己記錄, 進而才能根據但前狀態生成下一個數據. 爲了達到記錄當前狀態, 並配合 next() 函數進行迭代使用, 可以採用更簡便的語法, 即生成器. 生成器是一類特殊的迭代器

2. 創建生成器的方法

  1. 把一個列表推導式的 “[ ]” 改成 “( )”

    # –*– coding: utf-8 –*–
    # @Time      : 2019/3/30 15:20
    # @Author    : Damon_duanlei
    # @FileName  : generator_test01.py
    # @BlogsAddr : https://blog.csdn.net/Damon_duanlei
    
    a = [i ** i for i in range(5)]
    b = (i ** i for i in range(5))
    print(a)
    print("*" * 50)
    print(b)
    
    

    執行結果:

    [1, 1, 4, 27, 256]
    **************************************************
    <generator object <genexpr> at 0x0000021CEA7F1B48>
    
  2. **只要在一個函數中有 ** yield 關鍵字那麼這個函數就不是一個函數, 而是生成器

    generator 非常強大. 如果推算的算法比較複雜, 用類似列表推導式的 for 循環無法實現的時候, 可以用函數實現.

    如在迭代器章節提到的斐波那契數列,在用迭代器實現的方式中, 要藉助幾個變量來保存迭代的狀態, 下面用生成器來實現.

    # –*– coding: utf-8 –*–
    # @Time      : 2019/3/30 15:50
    # @Author    : Damon_duanlei
    # @FileName  : generator_test02.py
    # @BlogsAddr : https://blog.csdn.net/Damon_duanlei
    
    
    def fib_generator(max_places):
        current_places = 0
        a, b = 0, 1
        while current_places < max_places:
            ret = a
            a, b = b, a + b
            current_places += 1
            yield ret
        return "finish"
    
    
    if __name__ == '__main__':
        generator = fib_generator(10)
        print(next(generator))
        print(next(generator))
        for i in generator:
            print(i)
    
    

    執行結果:

    0
    1
    1
    2
    3
    5
    8
    13
    21
    34
    

    在使用生成器實現的方法中, 將原本在迭代器 __next__ 方法中實現的基本邏輯放到一個函數中來實現, 只是將每次迭代返回數值的 return 換成了 yield ,此時新定義的函數便不在是函數, 而是一個生成器

    注意:

    用 for 循環調用 generator 時, 發現拿不到 generator 的 return 語句的返回值. 如果想要拿到返回值, 必須捕獲Stopiteration 錯誤, 返回值包含在Stopiteration的 value 中:

    # –*– coding: utf-8 –*–
    # @Time      : 2019/3/30 15:50
    # @Author    : Damon_duanlei
    # @FileName  : generator_test02.py
    # @BlogsAddr : https://blog.csdn.net/Damon_duanlei
    
    
    def fib_generator(max_places):
        current_places = 0
        a, b = 0, 1
        while current_places < max_places:
            ret = a
            a, b = b, a + b
            current_places += 1
            yield ret
        return "finish"
    
    
    if __name__ == '__main__':
        generator = fib_generator(10)
        try:
            while True:
                num = next(generator)
                print(num)
        except StopIteration as e:
            print("生成器返回值:{}".format(e.value))
    
    
    

    執行結果:

    0
    1
    1
    2
    3
    5
    8
    13
    21
    34
    生成器返回值:finish
    

總結

  • 使用了 yield 關鍵字的函數不再是函數, 而是生成器
  • yield 關鍵字有兩點作用:
    • 保存當前運行狀態(斷點), 然後暫停任務, 即 將生成器掛起
    • 將 yield 關鍵字後面表達式的值作爲返回值返回, 此時可以理解爲起到了 return 的作用
  • 可以使用 next() 函數讓生成器從斷點出繼續執行, 即喚醒生成器
  • python3 中的生成器可以使用 return 返回最終運行的返回值, 而python2 中的生成器不允許使用 return返回一個返回值
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章