Python中迭代器&生成器的“奇技淫巧“

写在前面

  • 和小伙伴们分享一些Python 迭代器生成器的笔记
  • 博文为《Python Cookbook》读书笔记整理
  • 博文内容涉及:
    • 不用for循环手动访问迭代器中的元素
    • 委托代理迭代(自定义可迭代对象如何迭代)
    • 用生成器创建新的迭代模式
    • 如何实现一个迭代协议
    • 反向迭代
    • 定义自定义行为的生成器函数
    • 对迭代器做切片操作
    • 对可迭代对象自定义行为过滤
    • 迭代所有可能的组合或排列
    • 以索引-值对的形式迭代序列
    • 同时迭代多个可迭代对象
    • 在不同的可迭代对象中进行合并迭代
    • 解构迭代(扁平化处理嵌套型的可迭代对象)
    • 合并多个有序迭代对象,再对整个有序迭代对象进行迭代
    • 用迭代器取代while循环
  • 食用方式:
    • 了解Python基本语法即可
  • 理解不足小伙伴帮忙指正

一厢情愿,就得愿赌服输。 ——八月长安《最好的我们》


迭代器和生成器

关于迭代器小伙伴们应该不陌生,但是生成器貌似是python特有的,

Python 的迭代器语法简单,部分思想和Java8 Stream API有类似的地方(当然,Python要比Java年长),引入lambda表达式,predicate,函数式编程,行为参数化等可以做很多事情,同时和JAVA一样,对迭代行为进行了语法封装。但是本质上还是通过调用可迭代对象的迭代器来实现。

Python 的生成器yield,通过yieldyield from语法,可以很简单处理一些深度遍历的问题。

学习环境版本

┌──[root@vms81.liruilongs.github.io]-[~]
└─$python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

手动访问迭代器中的元素

当你希望遍历一个可迭代对象中的所有元素,但是却不想使用 for 循环。

为了手动的遍历可迭代对象,使用 next() 函数并在代码中捕获 StopIteration 异常。比如,下面的例子手动读取一个文件中的所有行:

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/23 00:18:55
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""

# here put the import lib


def manual_iter():
    with open('/etc/passwd') as f:
        try:
            while True:
                line = next(f)
                print(line, end='')
        except StopIteration:
            pass
manual_iter()

执行输出

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt

StopIteration 用来指示迭代的结尾。然而,如果你手动使用上面演示的next() 函数的话,你还可以通过返回一个指定值来标记结尾,比如 None 。

    with open('/etc/passwd') as f:
        while True:
            line = next(f)
            if line is None:
                break
            print(line, end='')

下面的列表在数据的末尾插入一个None值,那么你可以利用上面的if line is None: break来提早的结束迭代,避免异常的捕获

>>> items = [1, 2, 3,None]
>>> it = iter(items)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

关于迭代器的原理,有三个必不可少的元素,

  • 一个需要迭代的列表items
  • 通过iter()方法来获取一个可迭代对象的迭代器
  • 通过next()方法来获取当前可迭代的元素
>>> items = [1, 2, 3]
>>> it = iter(items)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

委托代理迭代

当你构建了一个自定义容器对象,里面包含有列表、元组或其他可迭代对象。你想直接在你的这个新容器对象上执行迭代操作如何处理

所谓的委托代理迭代,即通过重写迭代对象的 __iter__魔法方法,增加新的迭代行为。而所谓的新迭代行为即将迭代操作代理到容器内部的对象上去。

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/24 00:20:38
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""

# here put the import lib


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)

通过运行我们可以看到

┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$./622.py
Node(1)
Node(2)

Python的迭代器协议需要 __iter__方法返回一个实现了next()方法迭代器对象

如果你只是迭代遍历其他容器的内容,你无须担心底层是怎样实现的。你所要做的只是传递迭代请求既可。

这里的iter()函数的使用简化了代码,iter()只是简单的通过调用s.__iter__()方法来返回对应的迭代器对象,就跟1en(s)会调用s.len()原理是一样的。

用生成器创建新的迭代模式

实现一个自定义迭代模式,跟普通的内置函数比如range() , reversed()不一样。

如果想实现一种新的迭代模式,使用一个生成器函数来定义它。下面是一个生产某个范围内浮点数的生成器:

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

一个函数中需要有一个 yield 语句即可将其转换为一个生成器。跟普通函数不同的是,生成器只能用于迭代操作。可以把生成器理解为函数中途的retuen, 函数块中的代码可以看做是一个流水线,那么yield就是流水线中某个环境给调用方法者的反馈,但是他并不会影响流水线。

在来看一个Demo

>>> def countdown(n):
...     print('Starting to count from', n)
...     while n > 0:
...         yield n
...         n -= 1
...     print('Done!')
...
>>> c = countdown(3)
>>> next(c)
Starting to count from 3
3
>>> next(c)
2
>>> next(c)
1
>>> next(c)
Done!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> c
<generator object countdown at 0x7fd33ac44200>
>>>

一个生成器函数主要特征是它只会回应在迭代中使用到的 next 操作。一旦生成器函数返回退出,迭代终止。

实现迭代协议

构建一个能支持迭代操作的自定义对象,并希望找到一个能实现迭代协议的简单方法

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/24 22:41:58
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""

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)

    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()

# Example

if __name__ == '__main__':

    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(3))
    child1.add_child(Node(4))
    child2.add_child(Node(5))
    for ch in root.depth_first():
        print(ch)

在这段代码中,depth_first() 方法是点睛之笔。它首先返回自己本身并迭代每一个子节点并通过调用子节点的depth_first() 方法 (使用yield from语句) 返回对应元素

   def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()
┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$vim 644.py
┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$chmod +x 644.py
┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$./644.py
 Node(0)
 Node(1)
 Node(3)
 Node(4)
 Node(2)
 Node(5)

Python 的迭代协议要求一个iter ()方法返回一个特殊的迭代器对象,这个迭代器对象实现了next ()方法并通过 StopIteration 异常标识迭代的完成。

反向迭代

反方向迭代一个序列

使用内置的reversed()函数,

>>> a= [1,2,3,4]
>>> for i in reversed(a):
...     print(i)
...
4
3
2
1
>>>

反向迭代仅仅当对象的大小可预先确定或者对象实现了 __reversed__() 的特殊方法时才能生效。如果两者都不符合,那你必须先将对象转换为一个列表才行.

>>> f = open('/etc/passwd')
>>> for line in reversed(list(f)):
...     print(line,end='')
...
opensips:x:997:993:OpenSIPS SIP Server:/var/run/opensips:/sbin/nologin
oprofile:x:16:16:Special user account to be used by OProfile:/var/lib/oprofile:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
......

自定义实现反向迭代,通过在自定义类上实现__reversed()__ 方法来实现反向迭代。

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/24 22:41:58
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""

# here put the import lib


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

# Forward iterator
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1
# Reverse iterator
    def __reversed__(self):
        n = 1
        while n <= self.start:
            yield n
            n += 1



print(format('逆序','*>20'))
for rr in reversed(Countdown(5)):
    print(rr)
print(format('正序','*>20'))
for rr in Countdown(5):
    print(rr)

简单分析一下这个逆序的迭代器,魔法方法__iter__返回一个可迭代的对象,这里通过生成器来实现,当n>0的时候,通过生成器返回迭代元素。魔法方法__reversed__实现一个逆序的迭代器,原理和默认迭代器基本相同,不同的是,默认迭代器是默认调用,而逆向迭代器是主动调用

┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$./653.py
******************逆序
1
2
3
4
5
******************正序
5
4
3
2
1

定义带有额外状态的生成器函数

定义一个生成器函数,但是它会调用某个你想暴露给用户使用的外部状态值。

如果想让生成器暴露外部状态给用户,可以简单的将它实现为一个类,然后把生成器函数放到__iter__()方法中过去,简单来讲就是上面我们演示的代码,通过生成器来模拟next()方法行为

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/24 22:41:58
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""
# 这里的`deque(maxlen=N)创建了一个固定长度的双端队列
from collections import deque
def count(n):
   while True:
     yield n
     n += 1

class linehistory:
    def __init__(self, lines, histlen=3):
        self.lines = lines
        self.history = deque(maxlen=histlen)

    def __iter__(self):
        for lineno, line in enumerate(self.lines, 1):
            self.history.append((lineno, line))
            yield line

    def clear(self):
        self.history.clear()


if __name__ == "__main__":
    with open('/etc/services') as f:
        lines = linehistory(f)
        for line in lines:
            if '8080' in line:
                for lineno, hline in lines.history:
                    print('{}:{}'.format(lineno, hline), end='')

这里的deque(maxlen=N)创建了一个固定长度的双端队列,用于存放要保留的数据,把文件的所有的行数据存放到lines里,默认队列的大小是3,然后通过for循环迭代,在获取迭代器的方法里,我们可以看到通过enumerate来获取迭代对象和索引,然后放到队列里,通过yield模拟next方法返回迭代元素,所以队列里存放的默认为当前元素的前两个元素,

┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$./662.py
553:xfs             7100/tcp        font-service    # X font server
554:tircproxy       7666/tcp                        # Tircproxy
555:webcache        8080/tcp        http-alt        # WWW caching service
554:tircproxy       7666/tcp                        # Tircproxy
555:webcache        8080/tcp        http-alt        # WWW caching service
556:webcache        8080/udp        http-alt        # WWW caching service
┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$(cat -n  /etc/services  | grep -B2  -m 1 8080;cat -n  /etc/services  | grep -B1  -m 2 8080)
   553  xfs             7100/tcp        font-service    # X font server
   554  tircproxy       7666/tcp                        # Tircproxy
   555  webcache        8080/tcp        http-alt        # WWW caching service
   554  tircproxy       7666/tcp                        # Tircproxy
   555  webcache        8080/tcp        http-alt        # WWW caching service
   556  webcache        8080/udp        http-alt        # WWW caching service

如果你在迭代操作时不使用 for 循环语句,那么你得先调用iter()函数获取迭代器,然后通过next来获取迭代元素

对迭代器做切片操作

得到一个由迭代器生成的切片对象,但是标准切片操作并不能做到。

函数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
>>> import itertools
>>> for x in itertools.islice(c,5,10):
...    print(x)
...
5
6
7
8
9
>>>

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

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

遍历一个可迭代对象,但是它开始的某些元素你并不感兴趣,想跳过它们

itertools 模块中有一些函数可以完成这个任务。 首先介绍的是itertools.dropwhile() 函数。使用时,你给它传递一个函数对象和一个可迭代对象。它会返回一个迭代器对象,丢弃原有序列中直到函数返回 True 之前的所有元素,然后返回后面所有元素。

类似一个过滤器,返回满足条件的数据

┌──[root@vms81.liruilongs.github.io]-[~/python_cookbook]
└─$tee temp.txt <<- EOF
> #sdfsdf
> #dsfsf
> #sdfsd
> sdfdsf
> sdfs
> EOF
#sdfsdf
#dsfsf
#sdfsd
sdfdsf
sdfs
>>> with open('temp.txt') as f:
...     for line in dropwhile(lambda line: line.startswith('#'),f):
...         print(line,end=' ')
...
sdfdsf
 sdfs
 >>>

如果你已经明确知道了要跳过的元素的个数的话,那么可以使用 itertools.islice() 来代替

>>> from itertools import islice
>>> items = ['a', 'b', 'c', 1, 4, 10, 15]
>>> for x in islice(items, 3, None):
...         print(x)
...
1
4
10
15
>>>

islice() 函数最后那个 None 参数指定了你要获取从第 3 个到最后的所有元素,如果 None 和 3 的位置对调,意思就是仅仅获取前三个元素恰恰相反,(这个跟切片的相反操作 [3:] 和 [:3] 原理是一样的)。

迭代所有可能的组合或排列

想迭代遍历一个集合中元素的所有可能的排列或组合

itertools模块提供了三个函数来解决这类问题。其中一个是itertools.permutations(),它接受一个集合并产生一个元组序列,每个元组由集合中所有元素的一个可能排列组成。也就是说通过打乱集合中元素排列顺序生成一个元组

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

使用itertools.combinations()可得到输入集合中元素的所有的组合

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

这里需要注意的一点,combinations必须要指定参与组合的元素个数

>>> for i in combinations(items):
...     print(i)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Required argument 'r' (pos 2) not found

以索引-值对的形式迭代序列

迭代一个序列的同时跟踪正在被处理的元素索引。

内置的 enumerate() 函数可以很好的解决这个问题:

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

可以指定索引

>>> for idx,item in enumerate(my_list,1):
...    print(idx,item)
...
1 a
2 b
3 c
>>>

在处理列表嵌套的元组的时候需要注意的问题

>>> data = [ (1, 2), (3, 4), (5, 6), (7, 8) ]
>>> for n, (x, y) in enumerate(data):
...     print(n,x,y)
...
0 1 2
1 3 4
2 5 6
3 7 8
>>> for n, x, y in enumerate(data):
...     print(n,x,y)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
>>>

同时迭代多个序列

同时迭代多个序列,每次分别从一个序列中取一个元素

为了同时迭代多个序列,使用 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。一旦其中某个序列到底结尾,迭代宣告结束。因此迭代长度跟参数中最短序列长度一致。

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

如果这个不是你想要的效果,那么还可以使用itertools.zip_longest()函数来代替。

>>> from itertools import zip_longest
>>> for i in zip_longest(xpts,ypts):
...         print(i)
...
(1, 101)
(5, 78)
(4, 37)
(2, None)
(10, None)
(7, None)
>>>

使用 zip()可以将两个list打包并生成一个字典:

>>> xpts = [1, 5, 4, 2, 10, 7]
>>> ypts = [101, 78, 37]
>>> dict(zip(xpts,ypts))
{1: 101, 5: 78, 4: 37}
>>>

zip() 会创建一个迭代器来作为结果返回。如果你需要将结对的值存储在列表中,要使用 list() 函数

>>> zip(xpts,ypts)
<zip object at 0x7f413b75e1c8>
>>> list(zip(xpts,ypts))
[(1, 101), (5, 78), (4, 37)]
>>> dict(zip(xpts,ypts))
{1: 101, 5: 78, 4: 37}
>>>

在不同的容器中进行合并迭代

想在多个对象执行相同的操作,但是这些对象在不同的容器中,你希望代码在不失可读性的情况下避免写重复的循环。

itertools.chain() 方法可以用来简化这个任务。它接受一个可迭代对象列表作为输入,并返回一个迭代器,有效的屏蔽掉在多个容器中迭代细节

>>> from itertools import chain
>>> a = [1, 2, 3, 4]
>>> b = ['x', 'y', 'z']
>>> for x in chain(a, b):
...     print(x)
...
1
2
3
4
x
y
z
>>>

其他的容器也可以

# Various working sets of items
active_items = set()
inactive_items = set()
# Iterate over all items
for item in chain(active_items, inactive_items):
# Process item

扁平化处理嵌套型的序列

将一个多层嵌套的序列展开成一个单层列表

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
@File    :   Untitled-1.py
@Time    :   2022/05/30 03:10:41
@Author  :   Li Ruilong
@Version :   1.0
@Contact :   1224965096@qq.com
@Desc    :   None
"""

# here put the import lib

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)

items = ['Dave', 'Paula', ['Thomas', 'Lewis']]
for x in flatten(items):
        print(x)

简单的梳理一下这个方法,利用递归和yield from 的语法,深度遍历所以的元素,返回的遍历的每个元素,需要说明isinstance(x, Iterable)用于判断某个元素是否可迭代。

额外的参数ignore_types和检测语句isinstance(x,ignore_types)用来将字符串和字节排除在可迭代对象外,防止将它们再展开成单个的字符。这样的话字符串数组就能最终返回我们所期望的结果了。比如:

┌──[root@vms81.liruilongs.github.io]-[~]
└─$./listtolist.py
1
2
3
4
5
6
7
8
Dave
Paula
Thomas
Lewis

关于语句yield from在你想在生成器中调用其他生成器作为子例程的时候非常有用,当然也可以通过for循环来实现

合并多个有序序列,再对整个有序序列进行迭代

有多个排序序列,想将它们合并后得到一个排序序列并在上面迭代遍历

我们可以通过heapq.merge() 函数来实现

>>> import heapq
>>> a = [1, 4, 7, 10]
>>> b = [2, 5, 8, 11]
>>> for c in heapq.merge(a,b):
...   print(c)
...
1
2
4
5
7
8
10
11
>>>

heapq.merge 可迭代特性意味着它不会立马读取所有序列。这就意味着你可以在非常长的序列中使用它,而不会有太大的开销,但是有一点要强调的是heapq.merge()需要所有输入序列必须是排过序的。特别的,它并不会预先读取所有数据到堆栈中或者预先排序,也不会对输入做任何的排序检测。它仅仅是检查所有序列的开始部分并返回最小的那个,这个过程一直会持续直到所有输入序列中的元素都被遍历完。

>>> import  heapq
>>> with  open('16943_26281_10022_20220523_DATA.txt','rt') as file1,\
... open('16943_26281_10022_20220523_DATA.txt','rt') as file2,\
... open('16943_26281_10022_20220523_DATA_2022.txt','wt') as file3:
...      for c in heapq.merge(file1,file2):
...           file3.write(c)
...

这个和前面的itertools.chain() 有些类似,同样是合并迭代容器,不同的是迭代方式不同,一个是简单的合并,一个是保持有序的合并

┌──[root@vms81.liruilongs.github.io]-[~]
└─$wc -l 16943_26281_10022_20220523_DATA_2022.txt
1810 16943_26281_10022_20220523_DATA_2022.txt
┌──[root@vms81.liruilongs.github.io]-[~]
└─$wc -l 16943_26281_10022_20220523_DATA.txt
905 16943_26281_10022_20220523_DATA.txt
┌──[root@vms81.liruilongs.github.io]-[~]
└─$

用迭代器取代while循环

使用 while 循环来迭代处理数据,因为它需要调用某个函数或者和一般迭代模式不同的测试条件。能不能用迭代器来重写这个循环呢?

>>> import  sys
>>> f = open('/etc/passwd')
>>> for i in iter(lambda  : f.read(10),''):
...     n = sys.stdout.write(i)
...
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi

iter函数一个鲜为人知的特性是它接受一个可选的callable 对象一个标记(结尾)值作为输入参数。当以这种方式使用的时候,它会创建一个迭代器,这个迭代器会不断调用callable对象直到返回值和标记值相等为止。

我们来看一下对应的方法,通过open函数获取当前文件的内容,我们一般会通过whit的方式来接收,这里我们通过iter函数,利用lambda表达式获取当前文件的内容,以及结束的标识,反复迭代输出文件内容。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山河已无恙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值