4000-9696-28

Python中的迭代器与可迭代对象

2023年01月13日 11:01供稿中心:北大青鸟总部

摘要: Python中的许多结构内置支持可迭代协议,会自动处理StopIteration异常,如for循环、拆包等。

说到Python编程语言,最令人印象深刻的应该就是它的易用性了。为了提供易用性,语言中封装了大量的常用数据结构、算法和类库,并创建了不少

与其他语言不同的概念。其中,大部分概念都非常容易理解。然而,仍有些概念比较相似,常常使初学者混淆,比如迭代器和可迭代对象。

有编程经验的开发者都知道,迭代(或称循环)是处理大量数据时非常常用的手段。


从普通对象到迭代器

查看下面一个常规的类定义:

class SimpleClass1:

pass

simple1 = SimpleClass1()

如果从simple对象获取数据:

next(simple1)

将会报错“TypeError: 'SimpleClass1' object is not an iterator”,这是因为simple1对象不是一个迭代器。


下面介绍Python中的可迭代协议。

如果要使一个对象成为一个迭代器,需要:

实现无参数的“__next__”方法,返回下一个数据;

当没有下一个数据时,抛出一个特殊的异常StopIteration。


那么,重新实现SimpleClass,如下:

class SimpleClass2:

def __init__(self, name):

self.name = name

self.current = 0

def __next__(self):

if self.current >= len(self.name):

raise StopIteration

nextval = self.name[self.current]

self.current += 1

return nextval

simple2 = SimpleClass2('abc')

重新使用next函数就可以获取数据了:

next(simple2) # 返回a

next(simple2) # 返回b

next(simple2) # 返回c

next(simple2) # 抛出异常 StopIteration

如上所示,迭代器可以成功返回数据,如预期那样。但是每次都使用next函数获取数据还是比较麻烦,更不用说还要去处理异常。


从迭代器到可迭代对象

如果在开发中,对象能够直接支持for循环来进行遍历,并且自动处理StopIteration异常,那么实际开发工作将会简单许多。

于是Python中引入了可迭代对象的概念,可迭代对象就是能够支持使用iter来获取迭代器的对象。我们可以在类中实现__iter__方法来支持iter函数:

class SimpleClass3:

def __init__(self, name):

self.name = name

self.current = 0

def __next__(self):

if self.current >= len(self.name):

raise StopIteration

nextval = self.name[self.current]

self.current += 1

return nextval

def __iter__(self):

print('__iter__方法被调用')

return self

simple3 = SimpleClass3('abc')

使用for循环打印元素:

for item in simple3:

print(item)

将会顺序输出 a, b, c三个元素,for循环语句会自动调用iter获取此可迭代对象的迭代器,并自动处理异常。


Python可迭代协议使用实例

以上就是Python中的可迭代协议。下面使用该协议仿照系统内置range实现一个简化版本的类SimpleRange,它支持返回从0到n(不包括)的整数值。

class _SimpleRange:

def __init__(self, n):

self.n = n

self.current = 0

def __iter__(self):

return self

def __next__(self):

"""支持获取下一个元素"""

if self.current >= self.n:

raise StopIteration # 当没有下一个元素时抛出异常

next_val = self.current # 保存当前值以便返回

self.current += 1

return next_val


class SimpleRange:

"""简化版本的range"""

def __init__(self, n):

"""初始化对象"""

self.n = n

def __iter__(self):

"""支持返回迭代器"""

return _SimpleRange(self.n)


simple_range = SimpleRange(10)

r = range(10)

assert list(simple_range) == list(r)

assert list(simple_range) == list(r) # 该断言会成功通过

上面的代码中,_SimpleRange实现了__next__方法,所以其对象是一个迭代器。而SimpleRange实现了_iter__方法,并且在其中返回一个新的_SimpleRange对象。SimpleRange是一个可迭代对象。

需要注意的是,在SimpleRange对象中每次调用iter都会返回一个全新的迭代器(即_SimpleRange对象),这就是上面代码中,第二个断言能够通过的原因。

下面看第二个例子,定义一个列表如下:

lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]

我们知道,lst是可迭代对象,所以可以使用iter函数获取其迭代器iter(lst)。而如果将同一个迭代器放入zip函数,可以同时分别从

同一个迭代器获取数据,即:

lst_iter = iter(lst)

assert list(zip(lst_iter, lst_iter, lst_iter)) == [(1, 2, 3), (4, 5, 6), (7, 8, 9)]

将上面的代码组合在一起,配合拆包则可以使用代码:

list(zip(*[iter(lst)]*3))

将列表 [1, 2, 3, 4, 5, 6, 7, 8, 9],转换为 [(1, 2, 3), (4, 5, 6), (7, 8, 9)]。


特殊的可迭代对象

除了标准的实现可迭代的方法(即实现__iter__方法)外,如果一个类实现了__getitem__方法,并且其索引是从0开始的整数,则

其对象也是可迭代对象。如:

class SimpleClass4:

def __init__(self, n):

self.n = n

def __getitem__(self, idx):

if idx < self.n:

return idx

raise StopIteration


总结

可迭代对象就是可以用来拿到迭代器的对象,而迭代器可以用来获取下一个数据。

可迭代对象实现了返回迭代器的__iter__方法或者使用从0开始的整数索引的__getitem__方法;迭代器实现了获取下一个元素的__next__方法,当没有下一个元素时,迭代器会抛出一个特殊的异常StopIteration。

Python中的许多结构内置支持可迭代协议,会自动处理StopIteration异常,如for循环、拆包等。

标签:
关于我们
公司简介
发展历程
青鸟荣誉
联系我们
加入我们
青鸟课程
BCVE视频特效课程
BCUI全链路UI设计
BCSP软件开发专业
BCNT网络工程师
启能职业教育基础课程
学习客户端下载
青鸟优师
青鸟云课堂
微信 公众号 咨询 顶部 首页
官方新版意见收集

*

官方新版意见收集

提交成功,感谢您的反馈。

我们会认真阅读和考虑每个用户的反馈。