Composing Programs 2.4 Mutable Data - 03

伯克利CS 61A的教学用书笔记和一些课程笔记

2.4.7   Iterators

python和许多其他编程语言都提供一种按顺序处理容器中元素的方法,叫做迭代器(Iterators)。迭代器是一个对象,它提供对值的顺序访问。(Python and many other programming languages provide a unified way to process elements of a container value sequentially, called an iterator. An iterator is an object that provides sequential access to values, one by one.)

对于任何容器,比如list,可以通过内置的iter函数获得一个迭代器。可以通过内置的next函数访问迭代器中的内容。

>>> 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

每次调用next,迭代器中的元素都会减少,当数量减少到0时,再调用next,Python会抛出StopIteration异常,表示没有更多可用值。

迭代器会维护局部状态以表示目前序列中的位置。每次调用next函数,位置都会前进。两个独立的迭代器可以跟踪同一个序列的两个不同位置。然而,同一个迭代器的两个不同名称会共享一个位置。(An iterator maintains local state to represent its position in a sequence. Each time next is called, that position advances. Two separate iterators can track two different positions in the same sequence. However, two names for the same iterator will share a position because they share the same value.)

>>> r = range(3, 13)
>>> s = iter(r)  # 1st iterator over r
>>> next(s)
3
>>> next(s)
4
>>> t = iter(r)  # 2nd iterator over r
>>> next(t)
3
>>> next(t)
4
>>> u = t        # Alternate name for the 2nd iterator
>>> next(u)
5
>>> next(u)
6

第二个迭代器t的位置变化没有影响到第一个迭代器s的位置。第一个迭代器的最后一个返回值是4,下一次的位置是5。另一方面,第二个迭代器下一次next将返回7,因为u和t是同一个迭代器的不同名称,对u调用next也会影响t。

>>> next(s)
5
>>> next(t)
7

在迭代器上调用iter函数将会返回该迭代器,而不是产生一个新的迭代器(一个副本)。所以当调用iter时,不用担心参数是一个迭代器还是一个容器(比如list),iter也可以对迭代器进行调用,产生这个迭代器的别名。

>>> v = iter(t)  # Another alterante name for the 2nd iterator
>>> next(v)
8
>>> next(u)
9
>>> next(t)
10

迭代器的实用性来源于这样一个情况:迭代器的底层数据序列不能显示地表示在内存中。它提供了一种机制,可以依次获取一个序列的值,但不需要存储这个序列的所有元素。相反,当迭代器请求下一个元素的时候,可以根据需要计算出来,而不是从已经存储好该序列所有数据的内存中取出来。(The usefulness of iterators is derived from the fact that the underlying series of data for an iterator may not be represented explicitly in memory. An iterator provides a mechanism for considering each of a series of values in turn, but all of those elements do not need to be stored simultaneously. Instead, when the next element is requested from an iterator, that element may be computed on demand instead of being retrieved from an existing memory source.)

比如 range,它能够得到一个均匀的序列的所有元素。迭代器能够提供更大范围的底层序列数据集,因为它不需要提供对底层序列任意元素的访问。它牺牲了随机访问的灵活性,但是得到了足够大的对数据的顺序访问支持,而很多情况下,我们只需要顺序的访问数据就足够程序的应用了。

2.4.8   Iterables

任何可以产生迭代器的值都称为可迭代值(iterable value)。在python中,能够输入iter函数的就是可迭代值。字符串(string),tuple,set,dictionary都是可迭代值。即使是无序集合,比如字典(dictionary),在生成迭代器时,也必须对其内容定义顺序。字典(dictionary)和集合(set)是无序的,因为程序员无法控制迭代的顺序,但是python在其规范中保证了它们的某些属性的顺序性。

>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> d
{'one': 1, 'three': 3, 'two': 2}
>>> k = iter(d)
>>> next(k)
'one'
>>> next(k)
'three'
>>> v = iter(d.values())
>>> next(v)
1
>>> next(v)
3

加入或者删除字典的键(key)都会改变字典的结构,所以用该字典生成的迭代器就会失效,因为未来的迭代器可能会改变其内容的顺序。另一方面,改变已经存在的键(key)的值(value)不会让迭代器失效或者改变迭代器内容的顺序。

>>> d.pop('two')
2
>>> next(k)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

for可以用于所有可迭代值或者迭代器。

>>> r = range(3, 6)
>>> s = iter(r)
>>> next(s)
3
>>> for x in s:
        print(x)
4
5
>>> list(s)
[]
>>> for x in r:
        print(x)
3
4
5

2.4.9   Built-in Iterators

一些内置的迭代器函数。

map

map(func, iterable)函数,返回迭代器,对可迭代对象 iterable的每一个元素x都应用func函数。

>>> 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]

filter

filter(func, iterable)函数,返回迭代器,返回的迭代器的内容是iterable中func函数判断为True的元素。接上面的代码段。

>>> f = lambda x: x < 10
>>> a = filter(f, map(double, reversed(s)))
>>> list(a)
*** 6 => 12 ***
*** 5 => 10 ***
*** 4 => 8 ***
*** 3 => 6 ***
[8, 6]

其他函数

zip函数将两个迭代器打包成对(pairs)。reversed函数将迭代器逆序。list函数将迭代器转换为list。

>>> t = [1, 2, 3, 2, 1]
>>> reversed(t) == t
False
>>> list(reversed(t)) == t
True
>>> t = reversed(t) 
>>> next(t) 
1
>>> next(t)
2
>>> next(t)
3
>>> next(t)
2
>>> next(t)
1
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> items = iter(zip(d.keys(), d.values())) # Call next(items)
>>> next(items) 
('a', 1)
>>> next(items)
('b', 2)
>>> next(items)
('c', 3)

2.4.10   Generators

生成器允许我们利用python解释器的特性,在任意序列,甚至是无线序列上定义迭代。是一种特殊的迭代器。生成器函数和常规的函数的区别在于,它们的函数体内没有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

调用next时,生成器中的函数体会被执行,直到遇到yield才停止,然后保存运行环境,等待下一次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

直到第一次调用next时,生成器才会开始执行它的函数语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值