python相关迭代操作

Python 2019-09-11 1080

由于自己的神操作,导致自己写了很久的2篇笔记被删了crying,现在写第3篇!
写在前面,当涉及迭代操作时,优先查看itertools库!
1.反向迭代
在python中已经有了内置的reversed函数实现反向迭代,但是只有对象实现了__reversed__方法或者可以确定待处理的对象的大小才可使用。如果对象的数量大,还会带来性能问题。通过自定义__reversed__对象可以实现反向迭代。

class Countdown(object):      
    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      


if __name__ == '__main__':      
    c = Countdown(5)      
    for x in c.__iter__():      
        print(x)  # 5 4 3 2 1      
    for i in c.__reversed__():      
        print(i)  # 1 2 3 4 5  

2.对迭代器作切片操作
对迭代器或生成器作切片操作时,使用itertools.islice函数。

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

if __name__ == '__main__':      
    c = count(0)      
    #如果你打算c[10:20],别想了,c是生成器      
    from collections import Iterator      
    it = itertools.islice(c, 10, 20)      
    # 如果将20替换为None,将表示除前10个元素之外的所有元素。      
    print(it)  # <itertools.islice object at 0x00000194FDB7B2C8>      
    print(isinstance(it, Iterator))  # True,它是一个迭代器      
    for i in itertools.islice(c, 10, 20):      
        print(i) # 10 11 ... 19 20  

注意:之所以可以进行切片,是因为它将之前的元素都丢弃了,如果你需要切片处之前的元素,应该提前保存元素。
3.跳过可迭代元素的前一部分元素
如果你进行迭代操作,但不想要前面的元素,可以使用itertools.dropwhile()函数,参数:1个函数,1个可迭代对象,return的迭代器会丢弃序列中前几个元素。
如果你打开一个文件,文件中包含注释:

# 445566677      
# 88866699      
# /+--52566      
# dsd      
......  

你不需要注释,可以这样写:

>>>from itertools import dropwhile      
>>>with open('/etc/passwd') as f:      
    for line in dropwhile(lambda line: line.startswith('#')):      
        print(line)      
>>> # 一直丢弃,直到没有#  

使用dropwhile函数的优势是,它只会丢弃满足条件的元素,直到有某个元素不满足条件为止,对后面满足的元素无影响。

4.迭代所有可能的组合
比如有一个数组a,a=[1,2,3],你希望获取该数组的所有组合,可以使用itertools库中的permutations()方法,该方法接收一个元素集合,将其中所有的元素重排列为所有可能的情况。(返回元组,结果为数组a中的元素的所有组合,但是没有相同项)

from itertools import permutations

a = [1, 2, 3]    
for i in permutations(a):    
    print(i)    

results:    
        (1, 2, 3)    
        (1, 3, 2)    
        (2, 1, 3)    
        (2, 3, 1)    
        (3, 1, 2)    
        (3, 2, 1)  

permutations还接收一个参数,该参数可以限定返回的排列长度。

for i in permutations(a, 2):    
    print(i)

    (1, 2)    
    (1, 3)    
    (2, 1)    
    (2, 3)    
    (3, 1)    
    (3, 2)  

使用itertools.combinations()方法可以产生输入序列中所有元素的全部组合形式。

from itertools import combinations

a = [1, 2, 3]    
for i in combinations(a, 2):    
    print(i)    

当参数为2时:(1, 2)    
            (1, 3)    
            (2, 3)    
当参数为3时:(1, 2, 3)  

该方法返回的内容,不考虑元素的顺序,即(1,2)与(2,1)内容相同。
若需要返回所有的可能性可以使用itertools.combinations_with_replacement方法。

for i in combinations_with_replacement(a, 3):    
    print(i)

结果:(1, 1, 1)    
        (1, 1, 2)    
        (1, 1, 3)    
        (1, 2, 2)    
        (1, 2, 3)    
        (1, 3, 3)    
        (2, 2, 2)    
        (2, 2, 3)    
        (2, 3, 3)    
        (3, 3, 3)  

5.以索引-值对的形式迭代序列
迭代一个序列但是希望记录当前元素的索引
使用enumerate()(枚举),enumerate()还接收一个参数,用于指定起始索引。

a = [a, b, c]    
for i in enumerate(a):    
    print(i)

(0, 'a')    
(1, 'b')    
(2, 'c')    
当指定另一个参数为1时,    
(1, 'a')    
(2, 'b')    
(3, 'c')  

*当在元组序列上使用enumerate时,注意元组不可以展开,否则报错。

a = [(1, 2), (3, 4), (4, 5)]    
for i in enumerate(a):    
    print(i)

(0, (1, 2))    
(1, (3, 4))    
(2, (4, 5))    

a = [(1, 2), (3, 4), (4, 5)]    
for i, (k, v) in enumerate(a):    
    print(i, k, v)    

    0 1 2    
    1 3 4    
    2 4 5    
若for i, k, v in enumerate(a):    
    #ValueError: not enough values to unpack (expected 3, got 2)  

6.同时迭代多个序列
使用zip可以迭代多个序列,zip对象返回的是一个迭代器。

a = ['a', 'b', 'c']    
b = [1, 2, 3]    
for i in zip(a, b):    
    print(i)

('a', 1)    
('b', 2)    
('c', 3)  

当a的元素大于b时,以b的数量为主。

a = ['a', 'b', 'c', 'd']    
b = [1, 2, 3]    
for i in zip(a, b):    
    print(i)

# 依然打印了3个    
('a', 1)    
('b', 2)    
('c', 3)  

如果你不希望仅返回少数的数据可以使用itertools.zip_longest()

from itertools import zip_longest

a = ['a', 'b', 'c', 'd']    
b = [1, 2, 3]    
for i in zip_longest(a, b):    
    print(i)    

# results:    
('a', 1)    
('b', 2)    
('c', 3)    
('d', None)  

当超过最小长度后,默认置为None。
zip可以比较方便的生成字典。

a = ['a', 'b', 'c']    
b = [1, 2, 3]    
print(dict(zip(a, b)))    
# {'a': 1, 'b': 2, 'c': 3}  

7.在不同的容器中迭代
需要对许多对象执行相同操作,但是这些对象包含在不同容器中,且希望避免写过多的循环。可以使用itertools.chain()简化操作,见下面例子。

from itertools import chain

a = ['a', 'b', 'c', 'd']    
b = [1, 2, 3]    
for i in chain(a, b):    
    print(i)    
# a b c d 1 2 3    

a = ['a', 'b', 'c', 'd']    
b = [1, 2, 3]    
c = ('f', 'g', 'h') # 支持不同的类型,传入多个序列    
for i in chain(a, b, c):    
    print(i)    
# a b c d 1 2 3 f g h  

8.合并多个有序序列,再对整个有序序列进行迭代
我们有一组有序序列,相对它们合并后的有序序列进行迭代。
可以使用heapq.merge()函数。

import heapq

a = [9, 8, 7]  
b = [1, 2, 3]  
c = ('f', 'g', 'h')  
for i in heapq.merge(a, b):  
    print(i)  

1  
2  
3  
9  
8  
7  

heapq.merge不会对提供的序列做一次性读取,因此,该函数可以处理非常长的序列,而且开销小。下方例子告诉可以合并两个有序的文件:

# 第一次知道with可以这样写!!!  
with open('sorted_file', 'rt') as file1,\  
    open('sorted_file_2', 'rt') as file2,\  
    open('merged_file', 'wt') as outf:  
    for line in heapq.merge(file1, file2):  
        outf.write(line)  

**重点强调:
heapq.merge要求suo'y所有输入的序列都是有序的,它不会将所有数据读取到堆中,或者预先做任何的排序操作,它也不会对输入做任何验证,以检查它们是否满足有序的要求。相反,它只是简单的检查每个输入序列中的第一个元素,将最小的那个发送出去。然后再从之前选择的序列中读取一个新的元素,重复该步骤,直到没有数据取完。

9.使用迭代器取代while循环
这是一个神奇的操作!
伪代码1:
涉及I/O操作:

CHUNKSIZE = 8192  
def reader(s):  
    with True:  
        data = s.recv(CHUNKSIZE) # recv指定缓冲区的大小  
        if data == b'':  
            break  
        process_data(data)  

使用iter()替换:

CHUNKSIZE = 8192  
def reader(s):  
    for chunk in iter(lambda: s.recv(CHUNKSIZE), b''):  
        process_data(chunk)  

内置函数iter()有一个特性,它可以选择性接受一个无参的可调用对象以及一个 结束标志 作输出,当以该种方式使用时,iter()会创建一个迭代器,然后重复调用,直到遇到结束标志,停止。

标签:Python

文章评论

评论列表

已有0条评论