1. 程式人生 > >guxh的python筆記:迭代

guxh的python筆記:迭代

1,可迭代物件iterator,迭代器iterator,生成器generator

可迭代物件iterable:實現了__iter__方法的類,iter()方法可以從iterable中獲取iterator。

迭代器Iterator:1)實現__iter__方法(返回自己)和__next__(獲取值)方法的類。2)生成器。

生成器generator:含yeild,生成器屬於迭代器。

上述三種類型都可作用於for迴圈。

list、dict、str雖然是iterable,卻不是iterator,為什麼呢?因為iterator表示的是一個數據流,可以被next()呼叫不斷返回下一個資料,直到沒有資料時丟擲StopIteration錯誤。可以把資料流看作一個序列,但我們無法提前知道序列長度,只有不斷通過next()進行下一個計算。iterator甚至可以表示無限大的資料流,list不可能無限大。

 

2,可迭代物件與迭代器的關係

關於可迭代與迭代器的關係,具體實現細節可參考“3實現可迭代的方法”中的經典版方法。

s = 'abcdefg'    # s是個iterator,<class 'str'>

s可以被迭代:

for i in s:
   print(i)

迭代的本質是從iterable獲取iterator(iterable的__iter__方法return了一個iterator),然後再不斷使用iterator的next()方法獲取值:

sit = iter(s) # it是個iterator,<class 'str_iterator'>
while True:
  try:
     print(next(sit))
  except StopIteration:
    break 

判斷是否可迭代,iterable和iterator都可以被迭代:

isinstance(s, collections.Iterable) # True
isinstance(sit, collections.iterable) # True

但s不能被next,sit可以被next,因為s是iterable沒有__next__,sit是iterator具有__next__

 

3,實現物件可迭代的方法

實現一個序列型別,接受輸入值x,返回從x到11的值。

 

3.1,方法一:python序列鴨子型別

python在嘗試迭代物件時,找不到__iter__就會去呼叫__getitem__,__getitem__實現從0開始的索引取值即可

class Foo:
    def __init__(self, data):
        self.data = data

    def __getitem__(self, i):
        return range(self.data, 11)[i]

 

3.2,方法二:經典版

iterable+iterator,構建Foo的iterator,缺點是程式碼量大。

關鍵點:iterable的__iter__返回iterator;iterator的__iter__返回self,__next__逐個取值

class Foo:
    def __init__(self, data):
        self.data = data

    def __iter__(self):    # iterable中的__iter__返回iterator
        return Foo_iterator(self.data)

class Foo_iterator:
    def __init__(self, data):
        self.data = data

    def __iter__(self):    # iterator中的__iter__返回自己
        return self

    def __next__(self):    # iterator實現__next__
        if self.data > 10:
            raise StopIteration
        else:
            num = self.data
        self.data += 1
        return num

看看Foo的例項和返回的iterator:

f = Foo(1)
fit = iter(f)
print(type(f))  # <class '__main__.Foo'>
print(type(fit))  # <class '__main__.Foo_iterator'>

對比下內建的iterable類,是不是完全一致:

s = 'abc'
sit = iter(s)
print(type(s))  # <class 'str'>
print(type(sit))  # <class 'str_iterator'>

r = range(10)
rit = iter(r)
print(type(r))  # <class 'range'>
print(type(rit))  # <class 'range_iterator'>

 

3.3,方法三:糟糕版

Foo自己實現__next__和__iter__,讓Foo既是iterable,也是自己的iterator,糟糕不推薦

class Foo:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return self

    def __next__(self):
        if self.data > 10:
            raise StopIteration
        else:
            num = self.data
        self.data += 1
        return num

  

3.4,方法四:generator版

用generator實現iterable中的__iter__方法

class Foo:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        for i in range(self.data, 11):
            yield i

看看Foo的例項和返回的iterator,發現iter()返回了一個generator:

f = Foo(1)
fit = iter(f)
print(type(f))    #  <class '__main__.Foo'>
print(type(fit))   #  <class 'generator'>

備註:Foo中的__iter__獲取資料時,用的是惰性獲取range(非惰性就是list(range(self.data, 11)))。一般推薦用惰性函式實現,例如用finditer替代findall。

 

3.5,方法五:生成器表示式

class Foo:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return (i for i in range(self.data, 11))

 

4,生成器函式

待補充