一文看懂python的迭代器和可迭代对象_一文看懂python的迭代器和可迭代对象_酷python的博客-csdn博客 0阅读1970年01月-程序员宅基地

技术标签: python  迭代器  可迭代对象  

阅读完本文,你将收获以下知识点:

  1. 什么是迭代器和可迭代对象,他们之间有什么关系
  2. 迭代器可以迭代的底层原理是什么
  3. 如何自定义可迭代对象和迭代器

迭代器和可迭代对象是两个非常难以搞懂的python概念,我试图用最轻松的方式为你揭开它们的神秘面纱,你也将在这个过程中学习到研究技术的方法。

1. 判断一个对象是不是可迭代对象,是不是迭代器

我研究的思路,第一步先找到一种办法,可以判断一个对象是不是可迭代对象,是不是迭代器。

如果我掌握了这种方法,那么我就可以用它来识别哪些是可迭代对象,哪些是迭代器,这样做非常关键,在我对这两个概念还不熟悉时,最起码能知道谁是,谁不是。

我知道python有着非常强大的自省能力,python有办法判断一个对象是不是函数,是不是类,是不是类里的方法,一定有办法判断一个对象是不是可迭代对象或者迭代器,果真,经过一番谷歌,我找到了判断的方法

from collections.abc import Iterable, Iterator

print(isinstance([1, 2, 3], Iterable))      # True
print(isinstance((1, 2, 3), Iterable))      # True
print(isinstance(set([1, 2, 3]), Iterable))      # True
print(isinstance("python", Iterable))       # True
print(isinstance({
    'a': 1}, Iterable))       # True
print("分割线----------------")
print(isinstance([1, 2, 3], Iterator))      # False
print(isinstance((1, 2, 3), Iterator))      # False
print(isinstance(set([1, 2, 3]), Iterator))      # False
print(isinstance("python", Iterator))      # False
print(isinstance({
    'a': 1}, Iterator))      # False

经过试验,常见的数据类型中,列表,元组,集合,字典,字符串都是可迭代对象,他们都不是迭代器。所有可以用for循环遍历的对象,都是可迭代对象,甚至包括文件对象

from collections.abc import Iterable, Iterator

f = open('test.py')
print(isinstance(f, Iterable))          # True
print(isinstance(f, Iterator))          # True

上面的代码给了我一个惊喜,终于找到了一个迭代器。

2. 什么是可迭代对象

前面的研究只是开胃菜,接下来的才是重点,可迭代对象的定义如下:
如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象

我们来验证一下这个定义是否成立

from collections.abc import Iterable, Iterator


class Color(object):

    def __init__(self):
        self.colors = ['red', 'white', 'black', 'green']

    # 仅仅是实现了__iter__ 方法,在方法内部什么都不做
    def __iter__(self):
        pass

color_object = Color()
# 判断是否为可迭代对象
print(isinstance(color_object, Iterable))       # True
# 判断是否为迭代器
print(isinstance(color_object, Iterator))       # False

你现在能理解我为什么在文章的第一小节里寻找可以判断对象是否为可迭代对象的原因了吧,我需要验证可迭代对象的定义,只有这样才算是把这个知识点吃透。

在上面的定义中,我只是定义了__iter__方法,方法内什么都没有做,可它符合可迭代对象的定义,不过我要强调,它是可迭代对象,然而却不能被迭代,至于原因,要等到阅读完全文以后才能揭晓。

3. 什么是迭代器

迭代器的定义如下:如果一个对象同时实现了__iter__方法和__next__方法,它就是迭代器

按照这个定义,我对第二小节中的Color类进行改造

from collections.abc import Iterable, Iterator


class Color(object):

    def __init__(self):
        self.colors = ['red', 'white', 'black', 'green']

    # 仅仅是实现了__iter__ 方法,在方法内部什么都不做
    def __iter__(self):
        pass

    def __next__(self):
        pass

color_object = Color()
# 判断是否为可迭代对象
print(isinstance(color_object, Iterable))       # True
# 判断是否为迭代器
print(isinstance(color_object, Iterator))       # True

改造后,color_object 是可迭代对象,也是迭代器,尽管它不能正常的工作,但这并不影响它的身份。同时我们也可以得出一个结论,迭代器一定是可迭代对象,因为迭代器要求必须同时实现__iter__方法和__next__方法, 而一旦实现了__iter__方法就必然是一个可迭代对象。但是反过来则不成立,可迭代对象可以不是迭代器。

4. 迭代器工作原理

接下来,我们要研究一下迭代器是如何工作的,它是怎样实现迭代的,首先,我们要认识一下内置函数iter

4.1 内置函数iter获得迭代器

iter函数的作用是从可迭代对象那里获得一个迭代器, 我们设计一个实验来验证这个说法

from collections.abc import Iterator

lst_iter = iter([1, 2, 3])
print(isinstance(lst_iter, Iterator))       # True

所言非虚,iter会返回一个迭代器

4.2 使用内置函数next遍历迭代器

内置函数next的功能是从迭代器那里返回下一个值,设计实验来验证它

from collections.abc import Iterator

lst_iter = iter([1, 2, 3])
print(next(lst_iter))       # 1

实践与理论完美结合,让我们多调用几次next函数

from collections.abc import Iterator

lst_iter = iter([1, 2, 3])
print(next(lst_iter))       # 1
print(next(lst_iter))       # 2
print(next(lst_iter))       # 3
print(next(lst_iter))       # StopIteration

前3次调用next函数都能正常工作,第4次会抛出StopIteration异常,迭代器里已经没有下一个值了。

现在,让我们来做一个总结,遍历迭代器需要使用next方法,每调用一次next方法,就会返回一个值,没有值可以返回时,就会引发StopIteration异常。

4.3 为什么迭代器不能重复使用

有了4.2的铺垫,你应当已经理解迭代器不能重复使用的原因,next方法永远返回下一个值,第一次调用时,返回的是第一个值,就本例而言是1,下一次调用时,下一个值是2,返回的必然是2。此时,如果你想从头遍历,该怎么办呢,很简单,使用iter函数重新获得一个迭代器。

from collections.abc import Iterator

lst_iter = iter([1, 2, 3])
print(next(lst_iter))       # 1
print(next(lst_iter))       # 2  到了这一步,你想从头开始遍历,那么重新获得一个迭代器使用

lst_iter_2 = iter([1, 2, 3])
print(next(lst_iter_2))     # 1
print(next(lst_iter_2))     # 2
print(next(lst_iter_2))     # 3

4.4 for 循环的工作原理

for循环的工作原理可以描述为如下动作:

  1. 使用iter获得可迭代对象的迭代器
  2. 反复对迭代器使用next方法
  3. 捕获StopIteration异常,退出循环

5. 自定义可迭代对象和迭代器

当我们对一个概念和底层原理足够了解后,我们一定要自己去实现它,一方面验证自己对理论的理解是否正确,一方面加深对底层原理的应用能力。在第3节,已经实现了一个Color类,它的实例既是可迭代对象,又是迭代器,但它不能工作,因为__iter__方法和__next__方法都没有具体实现

5.1 实现__iter__方法

如果可迭代对象实现了__iter__方法,那么内置函数iter会调用对象的__iter__方法方法返回一个迭代器,由于Color类实现了__next__方法,因此Color的实例也是迭代器,在__iter__方法里返回self即可。

class Color(object):

    def __init__(self):
        self.index = -1
        self.colors = ['red', 'white', 'black', 'green']

    def __iter__(self):
        self.index = -1
        return self

我增加了一个index属性,初始值设置为-1,对于这个属性的用途,你一会便知

5.2 实现__next__方法

我们使用内置函数next对迭代器进行遍历,在这个过程中,是在调用迭代器的__next__方法, 内置函数的作用是返回迭代器的下一个值,这个功能的实现,我们需要放在__next__方法中。

    def __next__(self):
        self.index += 1
        if self.index >= len(self.colors):
            raise StopIteration

        return self.colors[self.index]

怎样才能做到返回下一个值呢?在调用__next__方法时,我将索引index的值加1,这样下一次执行return self.colors[self.index]时就会返回下一个值,这里再一次解释了迭代器不能重复使用的原因。

5.3 用for循环遍历

class Color(object):

    def __init__(self):
        self.index = -1
        self.colors = ['red', 'white', 'black', 'green']

    def __iter__(self):
        self.index = -1
        return self

    def __next__(self):
        self.index += 1
        if self.index >= len(self.colors):
            raise StopIteration

        return self.colors[self.index]

color_object = Color()
for color in color_object:
    print(color)

程序输出结果

red
white
black
green

6. 迭代器的数量问题

6.1 只能获得一个迭代器

在第5节中,color_object是可迭代对象,iter(color_object)会返回一个迭代器,现在请思考,如果多次对color_object调用iter函数,所得到的迭代器是同一个还是多个?如果想不清楚,不妨实验一下

color_object = Color()
iter_1 = iter(color_object)
iter_2 = iter(color_object)
iter_3 = iter(color_object)

print(id(iter_1))       # 2683506554888
print(id(iter_2))       # 2683506554888
print(id(iter_3))       # 2683506554888

三个迭代器的内存地址相同,说明它是同一个对象,不论对color_object调用多少次,返回的都是同一个迭代器,这是因为__iter__方法方法里返回的是self,正是color_object本身。这样一来,iter能够从color_object那里获得迭代器永远只有一个。

这并不是bug,而是一个设计问题,文件对象也只有一个迭代器,新建一个脚本 test.py

f = open('test.py')
iter_1 = iter(f)
iter_2 = iter(f)

print(id(iter_1) == id(iter_2))    # True
print(next(iter_1))
print(next(iter_2))

对于这种设计,我是这样理解的,打开文件后,对文件对象的遍历是通过文件指针进行的,而文件指针只有一个。遍历到文件内容的第3行时,文件指针就指向了这里,如果有多个迭代器,就不得不在迭代器里维护自己遍历时的行号,多个迭代器同时工作,文件指针就要在文件里跳来跳去,而且最关键的是,文件内容遍历一次就应当获得了想要的内容,不应该多次遍历,想多次遍历,请多次打开。

6.2 可以获得多个迭代器

以列表为例,使用iter方法可以获得多个迭代器

lst = [1, 2, 3]
iter_1 = iter(lst)
iter_2 = iter(lst)

print(id(iter_1) == id(iter_2))   # False

迭代器iter_1 和 iter_2 不是同一个对象,他们可以分开同时进行遍历,互不影响。前面已经介绍了,列表是可迭代对象,不是迭代器,那么在列表的__iter__方法里,就不能像我在Color的__iter__方法里定义的一样,返回self实例,而是要创建出一个迭代器。

列表的__iter__方法是用C语言实现的,我们看不到源码,但根据前面的理论储备,我们可以模拟这个过程。

lst = [1, 2, 3]

class ListIterator:
    def __init__(self, lst):
        self.lst = lst
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index > len(self.lst):
            raise StopIteration

        return self.lst[self.index]


def my_iter(lst):
    return ListIterator(lst)

iter_1 = my_iter(lst)
iter_2 = my_iter(lst)
print(id(iter_1) == id(iter_2))   # False

ListIterator 的实现,和Color类的实现几乎完全相同。

7. 总结

7.1 什么是迭代器和可迭代对象,他们之间有什么关系

1.迭代器: 如果一个对象同时实现了__iter__方法和__next__方法,它就是迭代器

2.可迭代对象: 如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象

3.他们之间有什么关系: 迭代器一定是可迭代对象,反之则不成立,可迭代对象的__iter__方法必须返回一个迭代器

7.2 迭代器可以迭代的底层原理是什么

使用next函数可以返回迭代器下一个值,所谓迭代就是不停的调用next函数直到引发StopIteration异常,next函数内部调用迭代器的__next__方法,具体返回哪个值是由__next__来决定的

7.3 如何自定义可迭代对象和迭代器

根据迭代器和可迭代对象的概念,自行定义类即可,5,6两节都给出了具体实现示例。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/KWSY2008/article/details/119038947

智能推荐

CentOS离线安装最新版本Docker_centos离线安装docker 18.09.6-程序员宅基地

文章浏览阅读1.4w次,点赞5次,收藏32次。一、背景由于公司内部服务器只能连内网,即使建立局域网yum源,也无法使用yum在线安装Docker CE。支持在线安装的朋友们,直接参考CentOS在线安装Docker官方文档即可:https://docs.docker.com/install/linux/docker-ce/centos/目前最新版本是v18.09.6。二、准备工作1、根据官方文档对CentOS的要求:需要是..._centos离线安装docker 18.09.6

关于pycharm上opencv的安装和cv2 ‘cv2.’无代码提示问题的解决_pycharm用terminal下载opencv-程序员宅基地

文章浏览阅读1.9k次,点赞6次,收藏10次。一.pycharm下opencv的安装在pycharm下的Terminal输入pip install opencv-python也可以下载功能加强版pip install opencv-contrib-python添加了国内pip源的朋友们记得在命令行后面加上(以下是清华pip源)-i https://pypi.tuna.tsinghua.edu.cn/simple..._pycharm用terminal下载opencv

linux内核编译过程中出现两个错误的解决方法~!-程序员宅基地

文章浏览阅读639次。  /*************************** error 1 ******************************/ 在编译内核的过程中出现了如下的编译显示代码:   drivers/video/console/vgacon.c: In function 'vgacon_startup':   drivers/video/console/vgacon.c:510..._编译内核 efi_secure_boot undeclared 错误

SqlServer事务1.0_php 原生sqlserver事务-程序员宅基地

文章浏览阅读156次。事务事务的定义事务的使用场景事务的特性事务的语法实例讲解事务的定义事务就是被绑定在一起作为一个逻辑工作单元的SQL语句组,如果任何一个语句操作失败那么整个操作就被失败,进而回滚到操作前状态,或者是上个节点。为了确保要么执行,要么不执行,就可以使用事务。要将一组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔离性和持久性。事务的使用场景举个例子,我们经常会使用转账功能,转账的时候,是先减去转出自己账户的金额,然后再在指定转入账户的金额加上转出的金额。如果刚好这个时候转出的操作已经执行完成_php 原生sqlserver事务

jstack分析java应用线程阻塞实战_jstack 分析阻塞进程-程序员宅基地

文章浏览阅读1.1k次。问题描述:生产环境,有个查询交易提交后,一直转圈未响应。问题分析:1、其他操作都比较流畅,初步怀疑是有同步锁导致的线程阻塞2、使用jstack命令收集堆栈信息。进程号为7689,命令如下:jstack -l 7689 > aa.tdump参考http://www.cnblogs.com/nexiyi/p/java_thread_jstack.html中的操作,查找..._jstack 分析阻塞进程

mysql timestamp 差值_MySQL用 TIMESTAMPDIFF() 函数计算时间差-程序员宅基地

文章浏览阅读1.1k次。TIMESTAMPDIFF() 函数将计算两个日期或日期时间表达式之间的整数时间差。其中,我们可以通过参数指定时间差的单位,如:秒、分钟、小时等。语法:TIMESTAMPDIFF(interval,datetime1,datetime2)参数说明:interval:日期比较返回的时间差单位。可以使如下值:FRAC_SECOND:表示间隔是毫秒SECOND:秒MINUTE:分钟HOUR:小时DAY:..._mysql timestamp 作差

随便推点

双目测距、重构(基于MATLAB和opencv-python)_matlab 基于双目视觉进行三维重构-程序员宅基地

文章浏览阅读3k次。双目测距、重构楼主之前用的SFM来进行重构,但是得到的是视差图,点云和实物存在比例关系,单目的还是不能解决scale这个问题的。所以今天用双目的来进行重构,期间遇到了很多坑,实属难受。双目测距过程大致可以分为,标定,图像校正,计算视差,测距,知道这个流程,目标就很明确了标定opencv和matlab都有标定的代码,但是老师说还是matlab的标定更加准确,自己试验下来也的确感受到matla..._matlab 基于双目视觉进行三维重构

倒残差与线性瓶颈浅析 - MobileNetV2_倒残差结构-程序员宅基地

文章浏览阅读1.4w次,点赞18次,收藏112次。文章目录1 背景简介2 MobileNetV2 要点2.1 Inverted Residuals(倒残差结构)2.2 Linear Bottlenecks(线性瓶颈结构)3 代码实现 - pytorch1 背景简介  在提出 MobileNetV1 后,谷歌团队又于次年(2018 年)提出 MobileNetV2 网络。相较于 MobileNetV1, MobileNetV2 准确率更高,模型更小。关于 MobileNetV1 可参考本人先前博客(深度可分离卷积解析 - MobileNetV1),此处给_倒残差结构

移植opencv+opencv_contrib_apps/annotation/cmakefiles/opencv_annotation.dir/b-程序员宅基地

文章浏览阅读1.4k次,点赞4次,收藏8次。交叉编译opencv 3.4.2opencv_contribute 3.4.2ubuntu 16cmake 3.12.2cmake 进行编译配置勾选 ENABLE_CXX11设置安装路径 CMAKE_INSTALL_PREFIX /usr/local问题一:opencv-3.4.1/3rdparty/libpng/pngstruct.h:30:18: fatal error: zlib.h: No such file or directory~/op..._apps/annotation/cmakefiles/opencv_annotation.dir/build.make:99: recipe for t

图像分割最全综述_图像分割最优问题综述-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏26次。转载 https://www.cnblogs.com/CV-life/p/11160796.html 图像分割是计算机视觉研究中的一个经典难题,已经成为图像理解领域关注的一个热点,图像分割是图像分析的第一步,是计算机视觉的基础,是图像理解的重要组成部分,同时也是图像处理中最困难的问题之一。所谓图像分割是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域,使得这些特征在同一区域内表现出一致性或相似性,而在不同区域间表现出明显的不同。简单的说就是在一副图像中,把目标从背景..._图像分割最优问题综述

终于弄明白 i = i++和 i = ++i 了_i=i++-程序员宅基地

文章浏览阅读3.4w次,点赞156次,收藏618次。写在前面:前些天看完了JVM的内存结构,自以为自己是懂了,心里想想不就是分线程共享和线程私有嘛,然后又怎么怎么分怎么怎么的嘛…直到遇到了这道题目。说句实话,曾经自己做这种运算题目,完全是靠脑子空想,然后拿上笔颤抖的写下一个自己都不知道正不正确的答案。不过过了今天,我终于能确定它的答案了。为此,我也专门写一篇博客,记录我的学习!!!文章目录1、题目2、分析2.1、第一步2.2、第二步2.3、第三步2.4、第四步2.5、结果3、i = ++i1、题目package pers.mobian.._i=i++

Beginning ARC in iOS 5 Tutorial Part 2_warning[440]: the section alignment is less than 2-程序员宅基地

文章浏览阅读779次。Note from Ray: This is the twelfth iOS 5 tutorial in the iOS 5 Feast! This tutorial is a free preview chapter from our new bookiOS 5 By Tutorials. Matthijs Hollemans wrote this chapter – the same_warning[440]: the section alignment is less than 2^2. data in literal pool m

推荐文章

热门文章

相关标签