cs61a 課時筆記 迭代

筆記摘自:4.2Implicit Sequences

range

range能夠用來表示給定範圍的整數,但是它並不保存那些整數。如下,當要選取45006230的元素時,才計算10000+45006230作爲返回值。

>>> r = range(10000, 1000000000)
>>> r[45006230]
45016230

迭代器

迭代器能夠給提供一個接一個連續獲取值的功能。
使用內建函數iter和next函數實現,當沒有更多的元素可被獲取時,系統會報錯,可使用try-except不讓其報錯。

>>> primes = [2, 3, 5, 7]
>>> type(primes)
<class 'list'>
>>> iterator = iter(primes)
>>> type(iterator)
<class 'list_iterator'>
>>> next(iterator)
2
>>> next(iterator)
3
>>> next(iterator)
5
>>> next(iterator)
7
>>> next(iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> try:
        next(iterator)
    except StopIteration:
        print('No more values')
No more values

內建迭代器

map函數在調用時不會指定運算,而是創建一個迭代對象。當使用next函數取值時會返回值。

>>> def double_and_print(x):
        print('***', x, '=>', 2*x, '***')
        return 2*x
>>> s = range(3, 7)
>>> doubled = map(double_and_print, s)  # double_and_print not yet called
>>> next(doubled)                       # double_and_print called once
*** 3 => 6 ***
6
>>> next(doubled)                       # double_and_print called again
*** 4 => 8 ***
8
>>> list(doubled)                       # double_and_print called twice more
*** 5 => 10 ***
*** 6 => 12 ***
[10, 12]

類似的還有filterzip函數。

生成器

生成器最明顯的特點就是不同return返回值,而是使用yield返回值。生成器不使用對象的屬性在序列中跟蹤其進度。相反,它們控制生成器函數的執行,每次調用生成器上的next時,該函數將一直運行,直到執行下一個yield語句。

>>> def letters_generator():
        current = 'a'
        while current <= 'd':
            yield current
            current = chr(ord(current)+1)
>>> for letter in letters_generator():
        print(letter)
a
b
c
d

在線調試


yield聲明標識函數是一個生成器函數。每次調用生成器函數時,不返回特定的值,而是返回一個生成器。next函數調用生成器得到下一個值,只有當next函數調用時,生成器函數的函數體纔會被執行。

>>> letters = letters_generator()
>>> type(letters)
<class 'generator'>
>>> next(letters)
'a'
>>> next(letters)
'b'
>>> next(letters)
'c'
>>> next(letters)
'd'
>>> next(letters)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

如果我們有一個無線長的序列需要遍歷時,怎麼辦?或者說,如果我有一個很長的序列,但是它耗費很大的內存,這個問題怎麼解決。Python的流就提供了一個解決辦法。在一個list中,所有的值都預先存放在開闢的內存中,而在流中,我們只存放了第一個值,剩下的值只有在調用的時候纔會被計算得到並存放在內存中。這樣就大大減少了內存的開銷。

class Link:
    """A linked list with a first element and the rest."""
    empty = ()
    def __init__(self, first, rest=empty):
        assert rest is Link.empty or isinstance(rest, Link)
        self.first = first
        self.rest = rest
    def __getitem__(self, i):
        if i == 0:
            return self.first
        else:
            return self.rest[i-1]
    def __len__(self):
        return 1 + len(self.rest)

class Stream:
	"""A lazily computed linked list."""
    class empty:
        def __repr__(self):
            return 'Stream.empty'
    empty = empty()
    def __init__(self, first, compute_rest=lambda:empty):
        assert callable(compute_rest),'compute_rest must be callable.'
        self.first = first
        self._compute_rest = compute_rest
    @property
    def rest(self):
    """Return the rest of the stream, computing it if necessary."""
        if self._compute_rest is not None:
            self._rest = self._compute_rest()
            self._compute_rest = None
        return self._rest
    def __repr__(self):
        return 'Stream({0},<...>)'.format(repr(self.first))

r = Link(1, Link(2+3, Link(9)))
s = Stream(1, lambda:Stream(2+3, lambda:Stream(9)))
print(r.first)
print(s.first)
print(r.rest.first)
print(s.rest.first)
print(r.rest)
print(s.rest)

在上述的Stream中,self._rest初始值爲None.當使用點操作調用rest時,觸發self._rest = self._compute_rest(),且函數compute_rest觸發一次就被銷燬。@property詳細解析看這裏
下面定義一個無窮大的整數序列:

class Link:
    """A linked list with a first element and the rest."""
    empty = ()
    def __init__(self, first, rest=empty):
        assert rest is Link.empty or isinstance(rest, Link)
        self.first = first
        self.rest = rest
    def __getitem__(self, i):
        if i == 0:
            return self.first
        else:
            return self.rest[i-1]
    def __len__(self):
        return 1 + len(self.rest)

class Stream:
    class empty:
        def __repr__(self):
            return 'Stream.empty'
    empty = empty()
    def __init__(self, first, compute_rest=lambda:empty):
        assert callable(compute_rest),'compute_rest must be callable.'
        self.first = first
        self._compute_rest = compute_rest
    @property
    def rest(self):
        if self._compute_rest is not None:
            self._rest = self._compute_rest()
            self._compute_rest = None
        return self._rest
    def __repr__(self):
        return 'Stream({0},<...>)'.format(repr(self.first))

def integer_stream(first):
        def compute_rest():
            return integer_stream(first+1)
        return Stream(first, compute_rest)
        
positives = integer_stream(1)
for i in range(5):
    print(positives.rest.first)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章