# Python 淺談 iterable、iterator、sequence 如有錯誤歡迎糾正,小弟正在學習中 --- ## iterable與iterator ### iterable `iterable` 是無狀態的,可迭代的對象list、tuple、dictorytion)皆是 常用的for i in `iterable` ``` python numbers = [1,2,3] for n in numbers print(n) ``` > 💡在for loop 之前,會將`iterable`透過`iter()`轉成`iterator`,再根據`iterator`操作 > > 💡在for loop時,會檢查`iterable`是否實現`__next__`與`__getitem__`,如果沒有則會raise not iterable,關於`__getitem__`下面會說到 ### iterator `iterator` 是一個`object`且有置狀態的知道目前迭代位置,而`iterator`主要由兩個`magic method`實現就稱為`iterator` - `__iter__()`: 返回obejct自己, - `__next__()`: 返回集合中next element,如果下一個element為空則會raise `StopIteration Exception` 例子1: list透過`iter()`內建函數轉換成`iterator`,而返回的是`iterator`,並用next()逐一訪問element,當取不到下一個element則 rasise StopIteration exception ``` python numbers = [1, 2, 3] iterator = iter(numbers) print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) >>> 1 2 3 print(next(iterator)) StopIteration ``` > 💡next(iterable[, default]) 接受的參數是一個iterable,後者參數為取不到element時給予預設值 ### implement custom iterator(`__next__`) 也能建立自己的iterator class,需實作`__iter__`與`__next__` magic method ``` python class Countdown: def __init__(self, start): self.current = start def __iter__(self): return self def __next__(self): if self.current >= 0: num = self.current self.current -= 1 return num else: raise StopIteration countdown = Countdown(10) for num in countdown: print(num) >>> 10 9 8 7 6 5 4 3 2 1 0 ``` --- ### implement custom iterable(`__getitem__`) 實作`__getitem__`可使對象變成`iterable` 例子: 建立iterable class ``` python class Countdown: def __init__(self, start): self.start = start def __getitem__(self, index): if index < self.start: return self.start - index else: raise IndexError("Index out of range") countdown = Countdown(10) for i in countdown: print(i) ``` 如何驗證class是`iterable`只要call iter() function,沒有rasie exception,就是`iterable` ``` python iterator = iter(countdown) print(next(iterator)) >>> 10 ``` 也可以用`slice`對class進行操做 ``` python class Countdown: def __init__(self, start): self.start = start def __getitem__(self, index): if index < self.start: return self.start - index else: raise IndexError("Index out of range") countdown = Countdown(10) print(countdown[0]) print(countdown[1]) print(countdown[2]) print(countdown[-1]) >>> 10 9 8 11 ``` 看到上面例子你會發現為什麼-1卻是11? 這是因為實作上沒有注意到slice用負數取得index的情境,可以看到實作上的疏失 ``` python return self.start - index # 10 -(-1) = 11 ``` 為了要解決這問題以及可以使用slice,需要改變一下實作,宣告一個list將range存放進去,並在`__getitem__` return 指定index,其實就是在實作`Sequence Object`,加上`__len__`就完成`Custom Sequence`了 > 💡Python Sequence Obejct 須由`__getitem__`、`__len__`實作 ``` python class Countdown: def __init__(self, start): self.start = start self._range = self._create_range() def _create_range(self): range_list = [] while self.start > 0: range_list.append(self.start) self.start -= 1 return range_list def __getitem__(self, index): return self._range[index] def __len__(self): return len(self._range) countdown = Countdown(10) print(countdown[0:-1]) print(countdown[1]) print(countdown[2]) >>> [10, 9, 8, 7, 6, 5, 4, 3, 2] 9 8 ``` --- ### Squence vs Iterator #### 優點與缺點 - 好處在於實作上較節省記憶體,因不需要將所有數據存入記憶體 - 缺點是要取第n個值就要迭代n次值到取得為此 Squence #### 優點與缺點 - 好處在於實作上將透過index直接取值無須迭代到才取,以及slice的處理 - 缺點是實作上需要注意排序、slice以及記憶體空間 #### 結論 對於兩者權衡我在使用python大多數看到的案例都是為了節省記憶體空間,特別是在處理大量數據分析以及對讀取資料庫大量數據情境 --- ## Reference [1] [medium] Python進階技巧 (6) — 迭代那件小事:深入了解 Iteration / Iterable / Iterator / __iter__ / __getitem__ / __next__ / yield - https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-6-%E8%BF%AD%E4%BB%A3%E9%82%A3%E4%BB%B6%E5%B0%8F%E4%BA%8B-%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3-iteration-iterable-iterator-iter-getitem-next-fac5b4542cf4 [2] [Youtube] 【python】对迭代器一知半解?看完这个视频就会了。涉及的每个概念,都给你讲清楚!- https://www.youtube.com/watch?v=vfQdRp_PhhQ