# 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