python高级(迭代器、迭代工具)

Python提供了多个可帮助迭代序列(或其他可迭代对象)的函数,其中一些位于模块itertools中,但还有一些内置函数使用起来也很方便。

 

1. 并行迭代
有时候,你可能想同时迭代两个序列。假设有下面两个列表:
names = ['anne', 'beth', 'george', 'damon'] 
ages = [12, 45, 32, 102] 
如果要打印名字和对应的年龄,可以像下面这样做:
fori in range(len(names)): 
    print(names[i], 'is', ages[i], 'years old') 
i是用作循环索引的变量的标准名称。一个很有用的并行迭代工具是内置函数zip,它将两个序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内容,可使用list将其转换为列表。
>>> list(zip(names, ages)) 
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
“缝合”后,可在循环中将元组解包。
for name, age in zip(names, ages): 
 print(name, 'is', age, 'years old') 
函数zip可用于“缝合”任意数量的序列。需要指出的是,当序列的长度不同时,函数zip将在最短的序列用完后停止“缝合”。
>>> list(zip(range(5), range(100000000))) 
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)] 

 

2. 迭代时获取索引
在有些情况下,你需要在迭代对象序列的同时获取当前对象的索引。例如,你可能想替换一个字符串列表中所有包含子串'xxx'的字符串。当然,完成这种任务的方法有很多,但这里假设你要像下面这样做:
for string in strings: 
    if 'xxx' in string: 
          index = strings.index(string) # 在字符串列表中查找字符串
          strings[index] = '[censored]'
这可行,但替换前的搜索好像没有必要。另外,如果没有替换,搜索返回的索引可能不对(即返回的是该字符串首次出现处的索引)。下面是一种更佳的解决方案:
index = 0 
for string in strings: 
    if 'xxx' in string: 
         strings[index] = '[censored]' 
    index += 1 
这个解决方案虽然可以接受,但看起来也有点笨拙。另一种解决方案是使用内置函数enumerate。
for index, string in enumerate(strings): 
   if 'xxx' in string: 
       strings[index] = '[censored]'

这个函数让你能够迭代索引-值对,其中的索引是自动提供的。

 

3. 反向迭代和排序后再迭代
来看另外两个很有用的函数:reversed和sorted。它们类似于列表方法reverse和sort(sorted接受的参数也与sort类似),但可用于任何序列或可迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。
>>> sorted([4, 3, 6, 8, 3]) 
[3, 3, 4, 6, 8] 
>>> sorted('Hello, world!') 
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w'] 
>>> list(reversed('Hello, world!')) 
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H'] 
>>> ''.join(reversed('Hello, world!')) 
'!dlrow ,olleH' 
请注意,sorted返回一个列表,而reversed像zip那样返回一个更神秘的可迭代对象。你无需关心这到底意味着什么,只管在for循环或join等方法中使用它,不会有任何问题。只是你不能对它执行索引或切片操作,也不能直接对它调用列表的方法。要执行这些操作,可先使用list对返回的对象进行转换。

要按字母表排序,可先转换为小写。为此,可将sort或sorted的key参数设置为str.lower。
例如,sorted("aBc", key=str.lower)返回['a', 'B', 'c']。

 

4.迭代器
对于魔法方法,这里介绍__iter__,它是迭代器协议的基础。

1)迭代器协议
迭代(iterate)意味着重复多次,就像循环那样。使用for循环迭代过序列和字典,但实际上也可迭代其他对象:实现了方法__iter__的对象。

方法__iter__返回一个迭代器,它是包含方法__next__的对象,而调用这个方法时可不提供任何参数。当你调用方法__next__时,迭代器应返回其下一个值。如果迭代器没有可供返回的值,应引发StopIteration异常。你还可使用内置的便利函数next,在这种情况下,next(it)与it.__next__()等效。

这有什么意义呢?为何不使用列表呢?因为在很多情况下,使用列表都有点像用大炮打蚊子。例如,如果你有一个可逐个计算值的函数,你可能只想逐个地获取值,而不是使用列表一次性获取。这是因为如果有很多值,列表可能占用太多的内存。但还有其他原因:使用迭代器更通用、更简单、更优雅。下面来看一个不能使用列表的示例,因为如果使用,这个列表的长度必须是无穷大的!
这个“列表”为斐波那契数列,表示该数列的迭代器如下:
class Fibs: 
    def __init__(self): 
         self.a = 0 
         self.b = 1 
    def __next__(self): 
         self.a, self.b = self.b, self.a + self.b 
         return self.a 
    def __iter__(self): 
         return self 
注意到这个迭代器实现了方法__iter__,而这个方法返回迭代器本身。在很多情况下,都在另一个对象中实现返回迭代器的方法__iter__,并在for循环中使用这个对象。但推荐在迭代器中也实现方法__iter__(并像刚才那样让它返回self),这样迭代器就可直接用于for循环中。

更正规的定义是,实现了方法__iter__的对象是可迭代的,而实现了方法__next__的对象是迭代器。

首先,创建一个Fibs对象。
>>> fibs = Fibs() 
然后就可在for循环中使用这个对象,如找出第一个大于1000的斐波那契数。
>>> forf infibs: 
... if f > 1000: 
... print(f) 
...break 
... 
1597 
这个循环之所以会停止,是因为其中包含break语句;否则,这个for循环将没完没了地执行。

通过对可迭代对象调用内置函数iter,可获得一个迭代器。
>>> it = iter([1, 2, 3]) 
>>> next(it) 

>>> next(it) 

还可使用它从函数或其他可调用对象创建可迭代对象。

 

2)从迭代器创建序列
除了对迭代器和可迭代对象进行迭代(通常这样做)之外,还可将它们转换为序列。在可以使用序列的情况下,大多也可使用迭代器或可迭代对象(诸如索引和切片等操作除外)。一个这样的例子是使用构造函数list显式地将迭代器转换为列表。
>>> class TestIterator: 
...        value = 0 
...        def __next__(self): 
...             self.value += 1 
...             if self.value > 10: raise StopIteration 
...             return self.value 
...        def __iter__(self): 
...             return self 
... 
>>> ti = TestIterator() 
>>> list(ti) 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值