python学习笔记4.2-python高级之迭代器

迭代是Python中最强有力的特性之一,同时对编程人员来说,也是最难理解的一种用法。其实从高层次来看,迭代就是一种处理序列中元素的方式。通过自定义迭代对象可以定义自己的处理元素的方式,此外还可以从itertools中选择实用的迭代模式、构建特殊的生成函数等。 一般来说,可迭代的对象有很多种形式,比如序列,集合等。从本质上来说,迭代是重复反馈过程的活动,其目的是逼近所需目标和结果,每一次重复称为一次迭代,每一次迭代得到的结果称为下一次迭代的初始值。 递归是调用自己的一种机制,是调用自己的过程。 Python的迭代协议要求__iter__()返回一个特殊的迭代器对象,由该对象实现的__next__()方法完成实际的迭代。

1 手动访问迭代器中的元素

最简单的方法当然是用for循环:

f = open('test.txt')
for line in f:
    print(line)

遍历结束以后就会自动退出循环。 除了for循环,还可以用next()函数,然后自己编写代码来捕捉StopInteration异常,同样是打开文件:

with open('test.txt') as f:
    try:
        while True:
            line = next(f)
            print(line)
    except StopIteration:
        print('超出范围')

在迭代结束以后,屏幕就会打印‘超出范围’,便于异常管理以及调试程序。一般来说,stopiteration异常时用来提醒我们迭代结束的,所以我们可以利用next()函数返回一个结束值。

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

自定义一种迭代模式,按照自己定义的迭代模式运行。一般我们需要利用生成器函数来定义迭代模式:

def frange(start,stop,increment):
    x = start
    while x < stop:
        yield x
        x += increment

for n in frange(0,5,0.8):
    print(n)

屏幕输出:

0
0.8
1.6
2.4000000000000004
3.2
4.0
4.8

函数中只要出现了yield语句就会将其转换为生成器。与普通函数不同的是生成器只会在响应迭代操作的时候才能运行。

3 实现迭代协议

构建一个自定义的对象,希望能够支持迭代操作,也就是实现一种迭代协议。 目前来看,要在对象上实现可迭代的功能,最简单的方式就是使用生成器函数。 接下来用一个迭代器能够以深度优先的模式遍历树的节点。

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

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

    def add_child(self,node):
        self._child.append(node)
    def __iter__(self):
        return iter(self._child)
    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()

if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child3 = Node(3)
    child4 = Node(4)
    child5 = Node(5)
    child1.add_child(child3)
    child2.add_child(child4)
    child3.add_child(child5)
    for ch in root.depth_first():
        print(ch)

4 反向迭代

有时候,我们需要反向迭代序列中的元素,这时候我们就可以利用内建的reversed()函数来实现反向迭代。

a = list(range(0,5))
for x in reversed(a):
    print(x)

反向迭代只有在待处理的对象拥有可确定的大小,或者实现了__reversend__()特殊方法时才能使用。如果这两个条件都无法满足,则只能先转换为列表。

f = open('test.txt')
for line in reversed(list(f)):
    print(line)
这是第10行 

这是第9行 

这是第8行 

这是第7行 

这是第6行 

这是第5行 

这是第4行 

这是第3行 

这是第2行 

这是第一行

我们可以在对象中实现__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
m = Countdown(5)
for i in iter(m):
    print(i)

for i in reversed(m):
    print(i)

5 对迭代器做切片操作

有时候我们需要对迭代器产生的数据做切片处理,但是普通的切片islice是不能用于迭代的,因为普通的切片操作需要提供可供索引的下标,也就是元素的序号,而迭代是不会记录元素序号的。这时候要么将迭代对象转换为列表后切片,要么借助于itertools.islice()函数完美的解决这个问题。

def count(n):
    while True:
        yield n
        n += 1
c = count(0)
b = c[10:15]
Traceback (most recent call last):
  File "D:/home/test_temp/test_generator.py", line 72, in <module>
    b = c[10:15]
TypeError: 'generator' object is not subscriptable

利用itertools.islice()函数:

def count(n):
    while True:
        yield n
        n += 1
c = count(0)
import itertools
b = itertools.islice(c,10,15)
print(list(b))

需要注意的是,itertoos.islice()函数会消耗掉所提供的迭代器中的元素,由于迭代器中的元素只能访问一次,因而itertools.islice()函数的实现方式是运行提供的迭代器,记录元素产生的索引号,丢弃所有起始索引之前的元素,记录之后的元素,知道到达结束索引为止。

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

在itertools模块中提供了一个itertools.dropwhile()函数来实现跳过可迭代对象前一部分元素。 打开这样一个文档,值保丢没有#号开头的行

#测试文件
#锦小年
#verison:1.0
#Python
这是第一行 
这是第2行 
这是第3行 
这是第4行 
这是第5行
from itertools import dropwhile
with open('test.txt') as f:
    for line in dropwhile(lambda line:line.startswith('#'),f):
        print(line)

达到的效果:

这是第一行 

这是第2行 

这是第3行 

这是第4行 

这是第5行

itertools.dropwhile()函数的第一个参数是要丢弃的元素的条件,只要它的值为True,对应的元素就会被丢弃。如果已知要丢弃元素的索引值,使用itertoos.islice()函数也可以实现这个功能。

7 迭代对象的排列和组合

7.1 迭代元素的全排列 itertools.permutations()函数能很好的实现迭代对象元素的全排列:

a = ['a','b','c']
from itertools import permutations
for p in permutations(a):
    print(p)

屏幕打印的结果:

('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

如果想得到较短长度的所有全排列,可以提供一个可选的长度参数:

a = ['a','b','c']
from itertools import permutations
for p in permutations(a,2):
    print(p)
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

7.2 产生输入序列中所有元素的全部组合形式 利用itertools.combination()函数可达到这个效果:

a = ['a','b','c']
from itertools import combinations
for p in combinations(a,3):
    print(p)

itertools.combinations()函数必须提供排列的长度参数,同时她是忽略顺序的,也就是(‘ a’,'b','c')和('a','c','b')是同一组合。 itertools模块给我们提供了很多强大实用的功能,所以在处理迭代对象的时候,首先去看看有没有相关对应的函数,可以给我们提供很多完美的解决方案

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

想迭代一个序列,并且记录序列中当前处理元素的索引,利用内建函数enumerate()可以解决这个问题。

a = ['a','b','c']
for index,val in enumerate(a):
    print(index,val)
0 a
1 b
2 c

如果要打印出规范的行号(从1开始而不是从0开始),可以传入一个start参数作为索引起始值:

a = ['a','b','c']
for index,val in enumerate(a,1):
    print(index,val)
1 a
2 b
3 c

这种情况特别适合跟踪记录文件中的行号,当想在错误信息中加上行号时就可以用到enumerate()函数:

with open('test.txt') as f:
    for index,line in enumerate(f,1):
        print('这是第%d行     '%index,line)

9 同时迭代多个序列

被迭代的元素在多个序列中,我们需要对其同时迭代。zip()函数是Python中的一个打包函数,其功能是将多个对象打包成一个元祖,例如有两个可迭代对象,zip之后就是一个(xi,yi)的元组对象。整个迭代长度和最短的输入序列长度相同。

x =list(range(0,10,2))
y =list(range(1,10,2))
c = zip(x,y)
print(list(c))
for a,b in zip(x,y):
    print(a,b)

如果说我想迭代长度和最长的输入序列相同,可以利用itertools.zip_longest()函数来实现。

x =list(range(0,10,2))
y =['a','b','c','f','m','hhhh']
c = zip(x,y)
from itertools import zip_longest
d = zip_longest(x,y)
print(list(d))
print(list(c))
[(0, 'a'), (2, 'b'), (4, 'c'), (6, 'f'), (8, 'm'), (None, 'hhhh')]
[(0, 'a'), (2, 'b'), (4, 'c'), (6, 'f'), (8, 'm')]

当然zip()函数还可以接受更多的参数,也还具有其他的很多功能,有待去试验。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据小魔方

左右用R右手Python9——字符串合并与拆分

在文本处理和数据清洗阶段,对字符串或者字符型变量进行分割、提取或者合并虽然谈不上什么高频需求,但是往往也对很重要的。 接下来跟大家大致盘点一下在R语言与Pyh...

3285
来自专栏Python入门

十年Python大牛花了三天总结出来的python基础知识实例,超详细!

6、变量在内存中是通过引用计数来跟踪管理的~想要一起学习Python的可以加裙227-435-450,裙内有各种资料满足大家,欢迎加裙

2571
来自专栏我是攻城师

理解桶排序算法原理

计数排序,基数排序,桶排序是所有排序算法里面时间复杂度能达到O(N)级别的算法,这主要原因是因为他们不采用基于比较的算法,前面的文章已经介绍了计数排序的原理,本...

1594
来自专栏编程

Python 迭代器和生成器

来源:田小计划 www.cnblogs.com/wilber2013/p/4652531.html 在Python中,很多对象都是可以通过for语句来直接遍历的...

18210
来自专栏生信小驿站

R语言字符串处理①R语言字符串合并与拆分

762
来自专栏开源优测

Python3快速排序

Python3快速排序 概述 快速排序(Quicksort)是对冒泡排序的一种改进。 快速排序由C. A. R. Hoare在1962年提出。 通过一趟排序将要...

3346
来自专栏Python小屋

Python中else关键字的常见用法

Python中的else常见用法有三:选择结构、循环结构和异常处理结构。 (1)选择结构 这应该是最常见的用法,与关键字if和elif组合来使用,用来说明条件不...

26810
来自专栏偏前端工程师的驿站

JS魔法堂:再次认识Function.prototype.call

一、前言                                                                大家先预计一下以下四个函...

20510
来自专栏静默虚空的博客

排序算法系列

概述 概念 排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。 排序分为内部排序和外部排序。 若整个排序过程不需要访问...

1997
来自专栏Python小屋

详解Python中的生成器表达式(generator expression)

生成器表达式(generator expression)也叫生成器推导式或生成器解析式,用法与列表推导式非常相似,在形式上生成器推导式使用圆括号(parenth...

3406

扫码关注云+社区