python迭代器与生成器

总结自python cookbook
本文主要介绍迭代器和生成器的常见用法,如有需要可以点击相应目录,书本完整内容可以搜索获取。

手动访问迭代器

用手动的方式调用迭代器可以让我们精确的把控程序的执行过程。手动访问需要使用next函数

def manual_iter():
    with open('/etc/passwd') as f:
        try:
            while True:
                line = next(f)  # 访问迭代器的下一个元素
                print(line, end='')
        except StopIteration:
            pass

委托迭代

当我们自己的类也想构建迭代器时可以使用委托迭代。Python的迭代协议要求定义一个__iter__(),将迭代请求委托到内部迭代器中。

class Node:
    def __init__(self, value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self, node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children) # 将迭代委托到列表中

# Example
if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    # Outputs Node(1), Node(2)
    for ch in root:
        print(ch)

使用生成器来进行迭代

生成器可以直接应用迭代器,帮助我们实现一种新的迭代模式。使用yield语句就会定义一个生成器。对于生成器可以直接使用next函数。

def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x
        x += increment
        
>>> for n in frange(0, 4, 0.5):   # 生成器为可迭代对象
...     print(n)
...
0
0.5
1.0
1.5
2.0
2.5
3.0
3.5

反向迭代

实现反向迭代可以使用内建的reversed()函数。对于自定义的类,实现反向迭代需要实现__reversed__()方法。

class Countdown:
    def __init__(self, start):
        self.start = start

    # 正向迭代时的迭代器
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

    #反向迭代时的迭代器
    def __reversed__(self):
        n = 1
        while n <= self.start:
            yield n
            n += 1

for rr in reversed(Countdown(30)):  # 反向迭代函数
    print(rr)
for rr in Countdown(30):# 正向迭代函数
    print(rr)

对迭代器的切片

迭代器没办法使用简单的切片操作。但是迭代器的功能实在是强大,这是就可以使用itertools中的islice来对迭代器进行切片。

>>> def count(n):
...     while True:
...         yield n
...         n += 1
...
>>> c = count(0)
>>> c[10:20]
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable

>>> # Now using islice()
>>> import itertools
>>> for x in itertools.islice(c, 10, 20):
...     print(x)
...
10
11
12
13
14
15
16
17
18
19
>>>

跳过可迭代对象的前一部分元素

这里跳过读取有两种方法,1.使用dropwhile 2.islice函数。dropwhile用于我们不知道需要跳过多少元素的情况。islice用于我们已经知道需要跳过多少元素。

# dropwhile例子
>>> from itertools import dropwhile
>>> with open('/etc/passwd') as f:
...     for line in dropwhile(lambda line: line.startswith('#'), f):  # 跳过所有注释行
...         print(line, end='')
...
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
...
>>>

# isllice例子
>>> from itertools import islice
>>> items = ['a', 'b', 'c', 1, 4, 10, 15]
>>> for x in islice(items, 3, None): # 3为起始索引,这句话的意思是读取除前三个之外的所有元素
...     print(x)
...
1
4
10
15
>>>

利用迭代器获得排列组合

有时候我们需要获得一组元素的全排列,itertools自带了能够实现这种功能的工具。实现全排列利用itertools 中的 permutations。实现全部组合利用itertools中的combinations。

>>> items = ['a', 'b', 'c']
>>> from itertools import permutations
>>> for p in permutations(items):
...     print(p)
...
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
>>>
# 如果想要指定元素,可以添加相应的参数
>>> for p in permutations(items, 2):  # 获得前两个元素的全排列
...     print(p)
...
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')
>>>

当我们需要的是元素的组合时,就可以用到 combinations函数。

>>> from itertools import combinations
>>> for c in combinations(items, 3):
...     print(c)
...
('a', 'b', 'c')

>>> for c in combinations(items, 2):
...     print(c)
...
('a', 'b')
('a', 'c')
('b', 'c')

>>> for c in combinations(items, 1):
...     print(c)
...
('a',)
('b',)
('c',) 

迭代时获得索引

如果我们需要在迭代的同时获得索引,那么可以使用enumerate函数。这样就不用为for循环无法获得元素索引而发愁了。

>>> my_list = ['a', 'b', 'c']
>>> for idx, val in enumerate(my_list):
...     print(idx, val)
...
0 a
1 b
2 c

同时迭代多个对象

有时我们需要对很多对象进行迭代,这个时候可以zip函数来实现。

>>> xpts = [1, 5, 4, 2, 10, 7]
>>> ypts = [101, 78, 37, 15, 62, 99]
>>> for x, y in zip(xpts, ypts):
...     print(x,y)
...
1 101
5 78
4 37
2 15
10 62
7 99
>>>

zip(a, b) 会生成一个可返回元组 (x, y) 的迭代器,其中x来自a,y来自b。 一旦其中某个序列到底结尾,迭代宣告结束。 **因此迭代长度跟参数中最短序列长度一致。**如果需要和最长的序列保持一致,那么就使用zip_longest()函数。

在不同的容器中进行迭代

itertolls.chain()可以实现在多个容器中进行迭代。其原理是将多个可迭代对象作为输入然后输出一个迭代器。有了这个方法我们就不需要写很多for循环了。

>>> from itertools import chain
>>> a = [1, 2, 3, 4]
>>> b = ['x', 'y', 'z']
>>> for x in chain(a, b): # 将a,b进行了“链接”
... print(x)
...
1
2
3
4
x
y
z
>>>

将迭代对象扁平化

有时我们需要处理列表套列表的对象。这时候使用yield from来实现扁平化。yield from 可以递归的返回对象。其原理是将可迭代对象作为种子例程进行递归。

from collections import Iterable

def flatten(items, ignore_types=(str, bytes)):
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ignore_types):
            yield from flatten(x) #返回子例程
        else:
            yield x

items = [1, 2, [3, 4, [5, 6], 7], 8]
# Produces 1 2 3 4 5 6 7 8
for x in flatten(items):
    print(x)

在上面代码中, isinstance(x, Iterable) 检查某个元素是否是可迭代的。 如果是的话, yield from 就会返回所有子例程的值。最终返回结果就是一个没有嵌套的简单序列了。

合并有序序列

合并有序序列可以使用heapq.merge()函数,其输入两个有序序列之后返回一个有序序列。heapq.merge()在合并的时候并不会将提供的序列一次性读取。

>>> import heapq
>>> a = [1, 4, 7, 10]
>>> b = [2, 5, 6, 11]
>>> for c in heapq.merge(a, b):
...     print(c)
...
1
2
4
5
6
7
10
11
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值