python迭代器&生成器使用技巧(2):切片、遍历、索引值、多序列、多容器对象

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shenziheng1/article/details/83536630

1. 迭代器切片

迭代器和生成器不能使用标准的切片操作,因为它们的长度事先并不知道(并且也没有实现索引)。 函数 islice() 返回一个可以生成指定元素的迭代器,通过遍历并丢弃直到切片开始索引位置的所有元素,然后开始一个个的返回元素,并直到切片结束索引位置。

import itertools
def count(n):
    while True:
        yield n 
        n += 1

c = count(0)
# print( c[10:20] ) 
# TypeError: 'generator' object is not subscriptable

for num in itertools.islice(c, 10, 15):
    print(num)
# >>> 10, 11, 12, 13, 14

这里需要强调的一点是 islice() 会消耗掉传入的迭代器中的数据。 而迭代器是不可逆的。 所以如果需要之后再次访问这个迭代器的话,就得先将它里面的数据放入一个列表中了。

2. 排列组合的迭代遍历

如果我们想迭代遍历集合中元素的所有可能的排列或组合,可以用itertools模块提供两函数来解决这类问题。

  • itertools.permutations() , 接受集合并产生一个元组序列,每个元组由集合中所有元素的一个可能排列组成。 也就是说通过打乱集合中元素排列顺序生成一个元组。如果想得到指定长度的排列,可以传递长度参数permutation(items, length)。
from itertools import permutations

items = ['a', 'b', 'c']
for sample in permutations(items):
    print( sample )
# >>> ('a', 'b', 'c')
#     ('a', 'c', 'b')
#     ('b', 'a', 'c')
#     ('b', 'c', 'a')
#     ('c', 'a', 'b')
#     ('c', 'b', 'a')
  • itertools.combinations() 得到输入集合中元素的所有的组合
from itertools import combinations
for sample in combinations(items, 3):
    print(sample)
# >>> ('a', 'b', 'c')

对于 combinations() ,元素的顺序已经不重要了。 也就是说,组合 ('a', 'b') 跟 ('b', 'a') 其实是一样的.

3. 序列上索引值迭代 enumerate()

如果我们想在迭代一个序列的同时跟踪正在被处理的元素索引时可以采用这种方法。这种情况在遍历文件时想在错误消息中使用行号定位时候非常有用。

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

遍历文件消息时,可以直接用于发现文件消息发生错误所处的位置(行号):

def parse_data(filename):
    with open(filename, 'rt') as f:
        for idx, line in enumerate(f,1): # from Line 1
            fields = line.split()
            try:
                # cnt = int (fields[1])
                pass
            except ValueError as e:
                print('Line{}:Parse error: {}'.format(idx, e))

parse_data('test.txt')

4. 同时迭代多个序列 zip()

如何同时迭代多个序列,每次分别从一个序列中取一个元素?zip()函数就是为了解决多个序列同时迭代而设计的。

  • zip() 最小迭代终止
xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99,56, 22]
for x, y in zip(xpts, ypts):
    print(x,y)
# >>> 1 101
#     5 78
#     4 37
#     2 15
#     10 62

zip(a, b) 会生成一个可返回元组 (x, y) 的迭代器,其中x来自a,y来自b。 一旦其中某个序列到底结尾,迭代宣告结束。 因此迭代长度跟参数中最短序列长度一致

  • zip_longest() 最大迭代终止
from itertools import zip_longest

xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99, 56, 22]
for x, y in zip_longest(xpts, ypts):
    print(x,y)
# >>> 1 101
#     5 78
#     4 37
#     2 15
#     10 62
#    None 99
#    None 56
#    None 22

zip() 函数对于同时处理两个、两个以上的序列非常有用。需要注意的一点是:zip() 会创建一个迭代器来作为结果返回。 如果需要将结对的值存储在列表中,要使用list() 函数

xpts = [1, 5, 4, 2, 10]
ypts = [101, 78, 37, 15, 62, 99, 56, 22]
print(zip(xpts, ypts))
# >>> <zip object at 0x0000000007505F88>
print(list(zip(xpts, ypts)))
# >>> [(1, 101), (5, 78), (4, 37), (2, 15), (10, 62)]

5. 不同集合上元素的迭代

如果我们想在多个对象执行相同的操作,但是这些对象在不同的容器中。在不失可读性的情况下如何避免写重复的循环?itertools.chain() 可以用来简化这个任务。 它接受可迭代对象列表作为输入,并返回一个迭代器,有效的屏蔽掉在多个容器中迭代细节。 

from itertools import chain

a = [1, 2]
b = ['x', 'y', 'z']
for x in chain(a, b):
    print(x)
# >>> 1 2 x y z
    
print(chain(a, b))
# >>> <itertools.chain object at 0x0000000008B18978>

print(list(chain(a,b)))
# >>> [1, 2, 'x', 'y', 'z']

itertools.chain() 接受一个或多个可迭代对象作为输入参数。 然后创建一个迭代器,依次连续的返回每个可迭代对象中的元素。 这种方式(chain(a,b))要比先将序列合并(a+b)再迭代要高效的多。a + b 操作会创建一个全新的序列并要求a和b的类型一致。 chian() 不会有这一步,所以如果输入序列非常大的时候会很省内存。 并且当可迭代对象类型不一样的时候 chain() 同样可以很好的工作。

文章参考《python3-cookbook》

猜你喜欢

转载自blog.csdn.net/shenziheng1/article/details/83536630