Python高频面试题——迭代器和可迭代对象_迭代器

 

无论是面试测试还是运维涉及到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)的方式,如下图:

Python高频面试题——迭代器和可迭代对象_迭代器_02

循环背后的原理又是什么呢?

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

原创不易,如果文章帮到了你,劳烦点赞转发!