无论是面试测试还是运维涉及到python编码岗位时,迭代器和可迭代对象都是绕不开的一个问题,本文对这两个概念进行重点讲解,本文从什么是迭代讲起,然后介绍迭代器和可迭代对象二者的区别,最后通过for 循环和自定义迭代器来加深读者对这两个概念的理解,只要认真阅读完文章,相信一定会帮助到大家,文章有点长,建议首收藏!
迭代
关于迭代,维基百科是这样子定义的:迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
在程序中,迭代是一种遍历集合元素的方式,我们最常用的迭代应用如下:
for i in [1,2,3]:
print(i)
输出:
1
2
3
可迭代对象(iterable)
只要实现 __ iter __ 方法或者实现 __ getitem __方法而且其参数从0开始索引,那么该对象就是可迭代对象(iterable)。
Python 中的大多数内置数据结构(容器)都是可迭代对象,比如list、dict、tuple、set、string。创建一个可迭代对象的实例如下:
class IterableDemo(object):
def __init__(self, components):
self.components = list(components)
def __iter__(self):
return iter(self.components)
V1 = IterableDemo([1, 2, 3])
for i in V1:
print(i)
输出
1
2
3
可以看到实例IterableDemo进行了迭代,该对象之所以能迭代,是因为实现了__ iter __ ()方法。当使用for循环时候,解释器会检查对象是否有__ iter __ ()方法,有的话就是调用它来获取一个迭代器。所以没有 __ iter __ ()方法但实现了__ getitem __ (),解释器会创建一个迭代器,尝试从0开始按顺序遍历元素。如果尝试失败,Python便会抛出TypeError错误。
看下面这个list的例子,l=[1,2,3]。这里 l是一个可迭代对象。
我们可以通过方法__iter__()和函数iter把list(可迭代对象)转变成list迭代器对象,代码如下
print(type(l))
print(type(l.__iter__()))
print(type(iter(l)))
输出
<class 'list'>
<class 'list_iterator'>
<class 'list_iterator'>
我们可以看到,list变成了list_iterator,证明list是可迭代对象!
a=123456
print(type(iter(a)))
输出
TypeError: 'int' object is not iterable
可见int 类型是不可迭代对象,也就是说调用iter(对象)函数,如果该对象不可迭代,就会抛出TypeError的错误。
迭代器(iterator)
Python 中的迭代器是一个可以迭代的对象,一个每次仅仅返回一个元素的对象。从技术上讲,Python 迭代器对象必须实现两个魔法方法:__iter__() 和 __next__()方法,统称为迭代器协议(iterator protocol)。
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用 in , not in 关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特列并不是所有的元素都放在内存)在Python中,常见的容器对象有:list、dequeue、set、dict、Counter、tuple、str等等。尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是 可迭代对象 赋予了容器这种能力,当然并不是所有的容器都是可迭代的(Bloom filter容器不可以迭代)。
我们执行以下代码,创建x 和y两个独立的迭代器。
l=[1,2,3]
x=iter(l)
y=iter(l)
print("y第一次调用:"+str(next(y)))
print("x第一次调用:"+str(next(x)))
print("y第二次调用:"+str(next(y)))
print("x第二次调用:"+str(next(x)))
输出
y第一次调用:1
x第一次调用:1
y第二次调用:2
x第二次调用:2
证明了
1. x、y是两个独立的迭代器,彼此不影响
2. 迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,调用next,每次都是按顺序获取对应的元素
那么究竟什么是迭代器呢?
迭代器是一个可以记住遍历位置的对象,其内部有一个状态用于记录迭代所在的位置,以便下次迭代时候能取出正确的元素。迭代器就像一个懒人一样,当你需要数据时候才会返回给你,否则就在等待下一次的调用。
迭代器技术主要应用在哪些地方呢?主要包括:
• for 循环
• 构建和扩展集合类型
• 逐行遍历文本文件
• 列表推导、字典推导和集合推导
• 元组拆包
• 调用函数时,使用*拆包
迭代器和可迭代对象总结
共性:他们都可以通过循环的形式进行迭代;
迭代器是可迭代对象, 但是可迭代对象不一定是迭代器,例如list是可迭代对象,但不是迭代器;
可以通过iter(可迭代对象)的方法来生成迭代器;
可迭代对象需要实现 __ iter __ 方法或者实现 __ getitem __方法而且其参数从0开始索引。注意可迭代对象不一定实现__ next__方法;
迭代器需要同时实现__ iter __ 方法和__next__ 方法,当使用next()函数时会调用__next__方法。
举一个经典的例子把
l=[1,2,3]
print(next(l))
输出error:
print(next(l))
TypeError: 'list' object is not an iterator
可见,list不是一个迭代器,因为他没有实现__next__方法
接下来执行代码,print(next(iter(l)))
输出
1
可见通过iter(l) 生成了迭代器,因此next操作可以顺利进行
for i in (iterable)的内部实现
在大多数情况下,我们不会一次次调用next方法去取值,而是通过 for i in (iterable)的方式,如下图:
循环背后的原理又是什么呢?
调用iter(容器对象,即可迭代对象,即上图中的x=[1,2,3])函数生成迭代器(如果调用成功)
通过迭代器的__next__()方法访问容器元素
如果没有元素了,将抛出StopIteration exception
循环捕获StopIteration exception后就会终止循环
自定义迭代器
如果想自定义迭代器,需要实现__iter_和__next__两个方法
from collections.abc import Iterable
class MyIterator:
last = 0
def __iter__(self):
return self
def __next__(self):
self.last += 1
if self.last > 5:
raise StopIteration
return self.last
my= MyIterator()
print(isinstance(my,Iterable))
for i in my:
print(i)
输出:
True
1
2
3
4
5
从输出结果可以看出my 是iterable对象,并完成了遍历工作!
大家可以自定义一个类,里面不实现__iter_和__next__两个方法,例如
class Demo:
pass
demo= Demo()
print(type(iter(demo)))
输出:
TypeError: 'Demo' object is not iterable
原创不易,如果文章帮到了你,劳烦点赞转发!
网友评论