最近在看Python基礎教學(第三版),是之前python課的課本,但是之前沒讀過,雖然python一直用得挺多,但重新讀讀收穫還挺大。這裡做個筆記。
先是迭代器是什麼並簡單實現一個迭代器,然後是實現了一些range()。後面本來想寫生成器和八皇后問題,但是發現了一些很不錯的部落格,比我能寫出來的好多了,把連結收藏在後面了。
迭代器是像迴圈一樣重複很多次,但不會像列表那樣一次性全部生成,而是需要用的時候再生成,就節省了記憶體資源。有時可能只想一個個地獲取值,而不是用列表一次性獲取,列表可能佔用太多記憶體,並且有時沒辦法使用列表,列表的長度會到無窮大。
簡單的說,迭代器類中:
根據上面描述可以簡單寫一個迭代器出來
class TestIterator:
value = 0
def __next__(self):
self.value += 1
if self.value > 10: # 到這個條件了沒有可返回的了就丟擲一個StopIteration異常
raise StopIteration
return self.value # 每次返回下一個
def __iter__(self): # 這裡返回它自己
return self
if __name__ == '__main__':
ti = TestIterator()
print(list(ti))
執行的結果是:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
嗯於是就能知道了,關於__iter__和__next__:
實現了方法__iter__的物件是可迭代的,而實現了方法__next__的物件是迭代器。
方法__iter__返回一個迭代器,它是包含方法__next__的物件。
檔案中也寫得很清楚了,呼叫__next__時,迭代器應返回其下一個值。如果迭代器沒有可供返回的值,則引發StopIteration異常。
iter返回一個迭代器物件,即object.__iter__()。如果指定了sentinel(哨兵),這個迭代器將不斷呼叫直到返回的是sentinel。
然後再來一個斐波那契數列的例子,迭代器物件可以被放進for迴圈。
class Fibs:
def __init__(self) -> None:
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
if __name__ == '__main__':
fibs = Fibs()
for f in fibs: # 首先會執行__iter__方法獲取返回值,就它自己,然後執行一次它的__next__方法,不斷迴圈。
if f > 1000:
print(f)
break
返回這個數列中第一個大於1000的數,是1597。
(base) eisen@pop-os:~$ python3.8
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> v1 = range(20)
>>> dir(v1)
['__bool__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index', 'start', 'step', 'stop']
這裡先看到v1裡面只有__iter__,v1為可迭代物件Iterable,讓v2 = v1.__iter__()
>>> v2 = v1.__iter__()
>>> dir(v2)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
可以看到v2既有__iter__又有__next__為迭代器物件。range就是執行v1的__iter__獲取到裡面的迭代器物件v2,再執行v2的__next__方法...
>>> v2.__next__()
0
>>> v2.__next__()
1
>>> v2.__next__()
2
>>> v2.__next__()
3
>>> v2.__next__()
4
>>> v2.__next__()
5
到這裡就能理解書上這段話了。
於是實現一下range就很簡單了,在實現的MyRange裡面,__iter__方法就生成一個IterRange類的迭代器物件。
class IterRange():
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration()
return self.counter
class MyRange():
def __init__(self, num):
self.num = num
def __iter__(self):
return IterRange(self.num)
v1 = MyRange(20)
for i in v1:
print(i)
輸出就是把0到19列印出來。
本來還想寫後面的生成器和八皇后問題,但是發現了一些很不錯的部落格,我就沒必要自己寫了。這裡收藏一下。
Python中生成器的原理,這一篇講了生成器的使用方法,和詳細的原理(裡面有原始碼)
深入理解Python中的生成器,這一篇寫了生成器的語法,以及它支援的方法close/send等。
python生成器和迭代器有這篇就夠了,這篇寫得很詳細,後面還有補充itertools庫的學習。
還記得八皇后的解法嗎,這篇講了個故事。
本文來自部落格園,作者:EisenJi,轉載請註明原文連結:https://www.cnblogs.com/eisenji/p/16462245.html