超深入了解掌握迭代器的知识点

简介: 超深入了解掌握迭代器的知识点

目录

🗣️引言

⚛️可迭代的

👥一、for循环针对的数据类型特点

👥二、可迭代的与可迭代协议

👤1、迭代的概念

👤2、认识dir内置函数和双下方法

👤3、 如何证明一个数据类型是可迭代的

👤​​​​​4、可迭代协议

👤5、拓展点:内置enumerate函数的用法

👤6、总结可迭代的知识点

⚛️迭代器

👥一、迭代器协议

👥二、迭代器秘密方法的使用

👥三、迭代器协议的验证

👥四、验证range()是可迭代对象还是迭代器%E6%98%AF%E5%8F%AF%E8%BF%AD%E4%BB%A3%E5%AF%B9%E8%B1%A1%E8%BF%98%E6%98%AF%E8%BF%AD%E4%BB%A3%E5%99%A8)

👥五、迭代器的好处

👥六、迭代器总结

🗣️小结


🗣️引言

假如给了你一个列表 lis = [1,2,3,4],叫你读取列表中的数据,那要如何做?

首先我们想到的是可能是索引读法,比如要读列表中的第二个元素可以用 lis[1]来读到,

其次那就是可以用最最常用的for循环来读取数据了,其中for循环的取值方式想必大家都应该也必须知道了,那就是———按顺序依次取值,既不关心取到的每一个值的位置,也不会跳过某一个值去取其他位置上的值。那此时想必你心中会有一个疑问那就是for循环为什么会这样取值,在for循环的内部究竟有哪一股神秘的代码驱使它这样工作呢?看完下面内容想必就有了答案!

带着这个疑问我们就来接触一下迭代器吧



  • ⚛️可迭代的

👥一、for循环针对的数据类型特点

在开始前我们先了解两个英语单词

其次我们再看下面这两段代码

第一段:我们先将一个列表进行for循环,得到以下结果

for i in [2,3,4,5]
    print(i)
输出结果:
2
3
4
5

第二段:换个思路我们将列表替换为数字2345那么结果会是什么呢?

for i in 2345:
    print(i)

输出结果:

结果竟然是报错:那我们再看报错内容的翻译:

 它说int对象是不可迭代的,那么我们现在应该了解到了能够使用for循环的数据类型应该要是可迭代的,那么究竟哪些数据类型时可迭代的呢,我们应该如何区分呢?

👥二、可迭代的与可迭代协议

👤1、迭代的概念

将一个数据集中所有的数据按照顺序“一个接着一个一次取出”这个过程就叫做迭代

👤2、认识dir内置函数和双下方法

  • dir内置函数:

python中的dir()函数dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。简而言之dir可以告诉我们所传入的数据类型(参数)所包含的所有内置使用方法

print(dir(list))    #告诉我列表拥有的所有方法
print(dir([1,2]))   #告诉我列表拥有的所有方法
#上面的两种方法虽然实参不同但函数返回值相同

输出结果:

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

  • 双下方法:

概念:像'__add__', '__class__', '__contains__'等含有双下划线的方法叫做双下方法

首先我们知道让两个列表相加的方法是:

print([1]+[2])
输出结果
[1,2]

其实还可以这样写:

print([1].__add__([2]))
输出结果:
[1,2]

这两者的联系是: 其实在函数运行时并不认识"+,- ,* , /"号,因为程序只认识代码,所以你使用"+"时号程序内部会自动帮你调用"__add__"这个函数方法。

👤3、 如何证明一个数据类型是可迭代的

要证明一个数据类型是否是可迭代的我们要用到collections.abc模块

其中可迭代的数据类型是:list、dic、str、set、range()、f = open()、enumerate

from collections.abc import Iterable
a = 135
c = {}
print(isinstance(a,Iterable))
print(isinstance(c,Iterable))
print(isinstance([],Iterable))
print(isinstance((1,2),Iterable))
print(isinstance({1,2,3},Iterable))
print(isinstance(list,Iterable))
输出结果:
False
True
True
True
True
False

找出这些数据类型的公共内置使用方法发现都有'__iter__',可得出结论:含有'__iter__'内置方法的数据是可迭代的(iterable)。

下面就是用集合的去重性找出这些数据类型的公共内置使用方法:

ret = set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range))
print(ret)

👤​​​​​4、可迭代协议

Python 迭代协议由__iter__方法与__next__方法构成,若对象具有__iter__方法,称该对象为“可迭代对象(iterable object)”也就是可跌代的。若对象具有__next__方法,称该对象为“迭代器(iterator)”。__iter__方法必须返回一个迭代器对象,__next__方法不断的返回下一元素,或者抛出StopIteration。__next__方法是 Python 迭代协议的核心,__iter__方法是迭代协议的辅助——将可迭代对象转换成迭代器。

在大多数情况下,可迭代对象会自动转换成迭代器,迭代操作本质是由“迭代器”负责——每次迭代输出一个元素,当无元素时,会抛出StopIteration。这种自动转换机制是造成“可迭代对象”与“迭代器“概念模糊的主要原因。 另一造成混淆的原因就是,对象即是可迭代对象,也是迭代器——当对象具有__iter__方法与__next__方法,并且__iter__方法返回自身,一个典型对象就是“文件对象”。

我们可以用上面的dir方法判断数据类型内部是否含有__iter__方法,代码如下:

print(1,'__iter__' in dir(int))
print(2,'__iter__' in dir(bool))
print(3,'__iter__' in dir(list))
print(4,'__iter__' in dir(dict))
print(5,'__iter__' in dir(str))
print(6,'__iter__' in dir(tuple))
print(7,'__iter__' in dir(range(1)))
print(8,'__iter__' in dir(enumerate([])))

输出结果:
1 False
2 False
3 True
4 True
5 True
6 True
7 True
8 True

👤5、拓展点:内置enumerate函数的用法

可能会疑问enumerate怎么用使用方法如下:

enumerate()是python的内置函数 enumerate在字典上是枚举、列举的意思 对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值 enumerate多用于在for循环中得到计数 例如对于一个seq,得到: (0, seq[0]), (1, seq[1]), (2, seq[2])

enumerate()返回的是一个enumerate对象

使用方法如下:

for i,k in enumerate( ['as','d','hh']):
    print(i,k)
print(enumerate( ['as','d','hh']))

输出结果:

👤6、总结可迭代的知识点

总结一下我们现在所知道的:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。

那么内部的__iter__方法做了什么事情呢?

print([].__iter__())

输出结果:
<list_iterator object at 0x000002A79C69F640>

执行了list([ ])的__iter__方法,我们好像得到了一个list_iterator,翻译后就是列表迭代器。

这下就明白了一个可迭代对象通过调用内部的__iter__方法就会变成一个迭代器



  • ⚛️迭代器

👥一、迭代器协议

迭代器协议:

        迭代器协议---内部含有__next__和__iter__方法的就是迭代器

从上面我们知道了一个可迭代对象通过调用内部的__iter__方法就会变成一个迭代器

[].__iter__()就是一个列表迭代器

我们来看看这个列表的迭代器比起列表来说实现了哪些新方法,这样就能揭开迭代器的神秘面纱了吧?

'''
dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,然后取差集。
'''
#print(dir([1,2].__iter__()))
#print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))

输出结果:
{'__length_hint__', '__next__', '__setstate__'}

由上面我们知道,迭代器比原类型多出来了{'__setstate__', '__length_hint__', '__next__'}方法,这就是迭代器的秘密

👥二、迭代器秘密方法的使用

上面我们知道迭代器比原类型多出来了{'__setstate__', '__length_hint__', '__next__'}方法

那么这些方法有什么用呢?

#__length_hint__()打印出的是元素个数
print([5,2,0,'swx'].__iter__().__length_hint__())

输出结果:
4

#__setstate__:可以指定开始取值的位置

#__next__取关键性的作用
l = [1,2,3]
iterator = l.__iter__()
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())

输出结果:
1
2
3

这三个方法中,能让我们一个一个取值的神奇方法是谁?没错!就是__next__

在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。

那接下来我们就用迭代器的next方法来写一个不依赖for的遍历。

l = [1,2,3,4]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)

输出结果:

为什么会报错呢?

如果我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。

这个时候,我们就要使用异常处理机制来把这个异常处理掉。

l = [1,2,3,4]
l_iter = l.__iter__()
while True:
    try:
        item = l_iter.__next__()
        print(item)
    except StopIteration:
        break

👥三、迭代器协议的验证

上面说迭代器协议---内部含有__next__和__iter__方法的就是迭代器

用以下代码验证

class A:
    def __iter__(self):pass
    #def __next__(self):pass

from collections.abc import Iterable
from collections.abc import Iterator
print(isinstance(A(),Iterator))
print(isinstance(A(),Iterable))

输出结果:
False
True

class A:
    def __iter__(self):pass
    def __next__(self):pass

from collections.abc import Iterable
from collections.abc import Iterator
print(isinstance(A(),Iterator))
print(isinstance(A(),Iterable))

输出结果:
True
True

👥四、验证range()是可迭代对象还是迭代器

from collections.abc import Iterator
print('__next__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__
print('__iter__' in dir(range(12)))  #查看'__next__'是不是在range()方法执行之后内部是否有__iter__

print(isinstance(range(100000000),Iterator))  #验证range执行之后得到的结果不是一个迭代器

输出结果
False
True
False

👥五、迭代器的好处

迭代器的好处:

  1. 从容器类型中一个一个取值,会把所有的值都取到。
  2. 节省内存空间

迭代器并不会在内存中在占用一大块内存 而是随着循环每次生成一个

每一次next每次给我一个

👥六、迭代器总结

迭代器的概念

迭代器协议---内部含有__next__和__iter__方法的就是迭代器

迭代器协议和可迭代协议

可以被for循环的都是可迭代的

可迭代的内部都有__iter__方法

只要是迭代器 一定可迭代

可迭代的.__iter__()方法就可以的到一个迭代器

迭代器中的.__next__()方法可以一个一个的获取值

for循环实际上就是在使用迭代器

iterator 可迭代对象

直接给你内存地址

#直接给你内存地址
print([].__iter__())

输出结果:
<list_iterator object at 0x00000148A66DF0D0>

#不给你判断是否可迭代
#猜测它为一个可迭代对象用于判断一个数据类型是否是可迭代的,然后判断是否能够使用for循环
print(range(10))

输出结果:
range(0, 10)

可迭代对象是与for循环挂钩,而迭代器是与next()函数挂钩的

for i in l:
    pass
'''
执行for循环拿到的每一个值其实都是在for循环内部执行了 :
        迭代器就是:iterator = l.__iter__()
        然后再执行:iterator.__next__()
        直到报错的时候就自动停止了循环
'''



🗣️小结

1、双下方法:__iter__,__next__很少直接调用的方,一般情况下是通过其他语法触发(for)
2、可以被for循环的都是可迭代的
3、可迭代的内部都有__iter__方法
4、只要是迭代器 一定可迭代
5、可迭代的.__iter__()方法就可以的到一个迭代器
6、迭代器中的.__next__()方法可以一个一个的获取值
7、for循环实际上就是在使用迭代器
迭代器的特点:
       1、很方便使用,且只能取所有的数据取一次
       2、节省内存空间 
相关文章
|
设计模式 Java 数据库连接
手写自定义迭代器,秒懂迭代器底层原理
迭代器模式的UML类图如下图所示。
97 0
|
4月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
27 1
|
4月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
70 0
|
7月前
|
编译器 C++
【C++进阶(三)】STL大法--vector迭代器失效&深浅拷贝问题剖析
【C++进阶(三)】STL大法--vector迭代器失效&深浅拷贝问题剖析
|
7月前
|
算法 程序员 C语言
【C++ 迭代器实现细节 】深入探索C++迭代器:从实现到整合
【C++ 迭代器实现细节 】深入探索C++迭代器:从实现到整合
186 0
|
C++ 容器
【C++初阶】十二、STL---反向迭代器的实现
目录 一、反向迭代器 二、反向迭代器的实现
151 0
【C++初阶】十二、STL---反向迭代器的实现
|
算法 安全 关系型数据库
深入探究C++中的仿函数和迭代器——提升你的STL技能
作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段&gt;——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的 作者主页:热爱编程的小K 专栏链接:c++ 欢迎各位→点赞 + 收藏 + 留言​ 总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 ———————————————— 版权声明:本文为CSDN博主「热爱编程的小K」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_72157449
|
C++ 容器
C++ STL学习之【反向迭代器】
适配器模式是 STL 中的重要组成部分,在上一篇文章中我们学习了 容器适配器 的相关知识,即 stack 与 queue,除了 容器适配器 外,还有 迭代器适配器,借助 迭代器适配器,可以轻松将各种容器中的普通迭代器转变为反向迭代器,这正是适配器的核心思想
189 0
|
JavaScript 前端开发 中间件
一文彻底搞懂迭代器与生成器函数
参考mdn上解释,迭代器是一个对象,每次调用next方法返回一个{done: false, value: ''},每次调用next返回当前值,直至最后一次调用时返回 {value:undefined,done: true}时结束,无论后面调用next方法都只会返回{value: undefined,done:true}
164 0
一文彻底搞懂迭代器与生成器函数
|
存储 PHP 开发者
lterator 迭代器|学习笔记
快速学习 lterator 迭代器
lterator 迭代器|学习笔记