Python三大器之迭代器

迭代器协议

  迭代器协议规定:对象内部必须提供一个__next__方法,对其执行该方法要么返回迭代器中的下一项(可以暂时理解为下一个元素),要么就引起一个Stopiteration异常以终止迭代。(当所有元素被取干净后其实内部就会自动触发Stopiteration

  可迭代对象是指对象内部必须提供一个__iter__方法,并且只要是可迭代对象那么就可以通过__iter__方法创造出该对象所专属的迭代器。

  注意:

   1.迭代器(Iterator):必须有__next__方法才能被叫做迭代器。

   2.可迭代对象(Iterable):必须有__iter__方法才能被叫做可迭代对象

  两者之间的关系:

   1.迭代器是可迭代对象,

   2.可迭代对象不一定是迭代器,

初识迭代器

什么是迭代器


  迭代器指的是迭代取值的一种工具。 迭代指的是一个重复的过程,每次重复都必须基于上一次的结果而继续,单纯的重复并不是迭代。

# ==== 迭代与非迭代的区别 ====

count = 0
while count < 5:
print(count)
count += 1
# 以上的每一次重复动作都是基于上一次重复动作,可以称之为迭代。

while 1:
input(">>>")
# 以上不可称之为迭代,下一次的重复动作与上一次没有任何关系。

为什么要有迭代器


  对于列表等具有索引的数据类型来说,取出他们的值可以使用索引。 而对于字典集合等等不提供索引的数据类型来说,Python必须为其提供一种能够不依赖于索引的取值方式。这种方式就被称为迭代器。

怎么使用迭代器


  迭代器可以通过循环进行取值。下面的章节将详细介绍迭代器及其内部原理,学完本章节你就能随心所欲的驾驭迭代器了。

迭代器详解

迭代器和可迭代对象的区别与关系


  1.迭代器(Iterator):必须有__next__方法才能被叫做迭代器。

  2.可迭代对象(Iterable):必须有__iter__方法才能被叫做可迭代对象

  基于这两句话,我们可以为学过的数据类型做一个区分:

[].__iter__() # list具有 __iter__ 方法,是可迭代对象
[].__next__() # list不具有 __next__方法,不是迭代器

# AttributeError: 'list' object has no attribute '__next__'

  他们的关系在于:

  通过可迭代对象的__iter__方法。可以为其创造一个专属的迭代器。

  示例如下:

# ==== 迭代器和迭代对象的区分 ====

from collections.abc import Iterable # 可以用于判断是否是可迭代对象
from collections.abc import Iterator # 可以用于判断是否是迭代器

li = [1, 2, 3, 4, 5]
# isinstance 可以用来判断一个对象是否属于另一个对象

print(isinstance(li, Iterable)) # True 判断是否属于可迭代对象,说明具有__iter__方法
print(isinstance(li, Iterator)) # False 判断是否属于迭代器,说明没有__next__方法。

li_iterator = iter(li) # iter方法就是调用对象内置的__iter__方法。

print(li_iterator) # <list_iterator object at 0x0000021AC6BB3AF0>

print(isinstance(li_iterator, Iterable)) # True
print(isinstance(li_iterator, Iterator)) # True

# 可以看到。iter方法就是为可迭代对象li创建出了一个专属迭代器。

数据类型迭代类型一览


  我们可以通过导入collections.abs下的包或者手动输入每种数据类型查看是否具有__next__方法的方式做一个总结:

# ==== 实验过程 ====

from collections.abc import Iterable # 可以用于判断是否是可迭代对象
from collections.abc import Iterator # 可以用于判断是否是迭代器

st = ""
print(isinstance(st,Iterable)) # True
print(isinstance(st,Iterator)) # False

li = [1,2,3]
print(isinstance(li,Iterable)) # True
print(isinstance(li,Iterator)) # False

tu = (1,2,3)
print(isinstance(tu,Iterable)) # True
print(isinstance(tu,Iterator)) # False

di = {"k1":"v1"}
print(isinstance(di,Iterable)) # True
print(isinstance(di,Iterator)) # False

se = {1,2,3}
print(isinstance(se,Iterable)) # True
print(isinstance(se,Iterator)) # False

with open(file="test.text",mode="w",encoding="utf-8") as f:
print(isinstance(f, Iterable)) # True
print(isinstance(f, Iterator)) # True

# === 了解 === Ps:以下创建方式均为创建出经过优化的可迭代对象。并非迭代器,更并非生成器。

ra = range(1,11)
print(isinstance(ra,Iterable)) # True
print(isinstance(ra,Iterator)) # False

di_keys = di.keys()
print(isinstance(di_keys,Iterable)) # True
print(isinstance(di_keys,Iterator)) # False

di_values = di.values()
print(isinstance(di_values,Iterable)) # True
print(isinstance(di_values,Iterator)) # False

di_items = di.items()
print(isinstance(di_items,Iterable)) # True
print(isinstance(di_items,Iterator)) # False

se_fro = frozenset(se)
print(isinstance(se_fro,Iterable)) # True
print(isinstance(se_fro,Iterator)) # False

  Ps:关于什么叫做经过优化的可迭代对象。会在结束迭代器和生成器的学习后专门开辟一章节来讲。

  五大基本容器数据类型(listtupledictsetstr)等都是属于可迭代对象。本身并不属于迭代器。

  文件句柄对象本身是属于迭代器。

  此外,range()方法产生的数据类型是属于经过优化的可迭代对象。 以及,dictkeysitemsvalues等方法产生的数据类型也是属于经过优化的可迭代对象。

循环与迭代器

while循环取值与迭代器的作用体现


  迭代器的作用在于:对于没有索引的数据类型,对其遍历取值必须通过迭代器下的__next__方法来完成。 并且迭代器中的所有值一旦被取出来,该迭代器将不能被二次应用。

# ==== 迭代器的多次迭代示例 ====

# 方式1 : 针对具有索引的数据类型取值,可以不依赖于迭代器
li = [1,2,3,4,5]
index = 0
while index < len(li):
print(li[index])
index += 1
print("针对具有索引的数据类型取值完毕 ---")


# 方式2 :针对不具有索引的数据类型取值,必须依赖迭代器下的__next__方法。
dic = dict(k1="v1",k2="v2",k3="v3")
dic_ator = dic.__iter__() # 创建出专属迭代器,迭代器具有__next__方法
while 1:
try:
print(dic_ator.__next__())
# 或者用 next(li_ator)
except Exception as e:
break

print(" ----> 第二次取值")

while 1:
try:
print(dic_ator.__next__())
# 或者用 next(li_ator)
except Exception as e:
break

# ==== 执行结果 ==== Ps:可以看到。一个迭代器将所有值全部取出后,再对其进行遍历已经取不出值了。

"""
1
2
3
4
5
针对具有索引的数据类型取值完毕 ---
k1
k2
k3
----> 第二次取值
"""

迭代器的多次迭代示例

for循环底层原理


  for循环本质就是while循环 为什么说它方便。

    1.使用for循环会自动的为被迭代的可迭代对象创建出一个专属的迭代器。

    2.for循环会不断的调用迭代器中的__next__方法。

    3.当迭代器中所有的值全部被__next__取出后在对其进行取值会出发StopIterator的异常,而for循环可以自动的来捕捉这种异常。

  为什么Python要将除开文件对象外的所有数据类型作为可迭代对象而不直接做成迭代器:迭代器无法重复使用

# ==== for循环的底层原理 ====

# for循环本质就是while循环
dic = dict(k1="v1",k2="v2",k3="v3")
dic_ator = dic.__iter__() # 第一件事:创建专属迭代器
while 1:
try: # 第三件事: 捕捉异常
print(dic_ator.__next__()) # 第二件事,不断执行该迭代器下的__next__方法
# 或者用 next(li_ator)
except Exception as e:
break

# 每次for循环都会完成上述三步骤,
# 这也是为什么Python要将除开文件对象外的其他数据类型作为可迭代对象的原因(便于重复利用)
for i in dic:
print(i)
print("第一次结束")
for i in dic:
print(i)
print("第二次结束")
# 如果循环的是一个纯粹的迭代器,那么第二次的for循环根本就不能读出任何内容。迭代器值空=迭代器死亡

for循环的底层原理

迭代器小结

优点


  1.为序列和非序列类型提供了一种统一的迭代取值方式。(序列就是有序的,支持索引的数据类型,非序列是无序的,不支持索引的数据类型,如dictset等等)

  2.惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用__next__来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。

  3.Python中为了能让一个容器类型能被多次取值。干脆将他们全部都做成可迭代对象,当对其调用for循环时会自动创建迭代器,每一次for循环都创建出一个新的迭代器。

缺点


  1、除非取尽,否则无法获取迭代器的长度

  2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行__next__方法直到值取尽,否则就会停留在某个位置,等待下一次调用__next__;若是要再次迭代同个对象,你只能重新调用__iter__方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。

  3、Python中的容器类型本身就占据一定的内存空间,如:[1,2,3]。对其转换为迭代器只是为了方便多次被for循环调用,所以对可迭代对象每次for循环时创建出迭代器这种做法并不会节省很多内存。

疑点 - 如何自定义迭代器


  我们目前创造迭代器的方式都是使用可迭代对象的__iter__方法,那么这种创建迭代器的方式无疑是脱裤子放屁。

    1. 诸如listtupledictsetdict等可迭代对象本身没经历任何优化,他们的存值占据了内存空间。

    2. 迭代器本身具有惰性求值的特点,目的就是为了节省内存,但是对上述可迭代对象创建专属迭代器只是为了能够让其被多次调用。

  我们怎么样才能自己创建一个迭代器,而不是通过上述的可迭代对象调用其__iter__方法再创建一个迭代器呢?答案在下一章的生成器中。

Python三大器之迭代器的更多相关文章

  1. Python三大器之生成器

    Python三大器之生成器 生成器初识 什么是生成器 生成器本身属于迭代器.继承了迭代器的特性,惰性求值,占用内存空间极小. 为什么要有生成器 我们想使用迭代器本身惰性求值的特点创建出一个可以容纳百万 ...

  2. Python三大器之装饰器

    Python三大器之装饰器 开放封闭原则 一个良好的项目必定是遵守了开放封闭原则的,就比如一段好的Python代码必定是遵循PEP8规范一样.那么什么是开放封闭原则?具体表现在那些点? 开放封闭原则的 ...

  3. python三大器之while,if,for循环

    一.for循环(遍历循环) 在Python你可能要经常遍历列表的所有元素,对每个元素执行相同的操作;对于包含数字的列表,可能要对每个元素进行相同的计算;在网站中,可能需要显示文章中的每个标题等等.某一 ...

  4. python基础(补充):python三大器之生成器

    生成器的定义 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后 ...

  5. python基础(补充):python三大器之装饰器

    函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): i = ...

  6. python三大器之装饰器的练习

    装饰器 加载顺序从下至上 执行顺序从上至下 ''' 多层装饰器 ''' def deco1(func): #func=deco2 def wrapper1(*args, **kwargs): '''t ...

  7. python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器。

    python三大神器之virtualenv   pip, virtualenv, fabric通称为pythoner的三大神器. virtualenv virtualenv------用来建立一个虚拟 ...

  8. python基础之迭代器协议和生成器

    迭代器和生成器补充:http://www.cnblogs.com/luchuangao/p/6847081.html 一 递归和迭代 略 二 什么是迭代器协议 1.迭代器协议是指:对象必须提供一个ne ...

  9. python基础之迭代器协议和生成器(一)

    一 递归和迭代 二 什么是迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前 ...

随机推荐

  1. HEARTBLEED 漏洞复现

    漏洞分析的两篇文章 https://blog.csdn.net/javajiawei/article/details/82429886 https://xz.aliyun.com/t/1771 set ...

  2. 分布式事务专题笔记(二)分布式事务解决方案之 2PC(两阶段提交)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 前面已经了解了分布式事务的基础理论,以理论为基础,针对不同的分布式场景业界常见的解决方案有2PC.TCC ...

  3. SpringBoot 及其 基本原理(一)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 前言: 分布式架构及微服务理念 1.SOA理念(思想) SOA :即 Service Oriented ...

  4. 使用Mac的Remote Desktop Manager连接ubuntu16.04 & Win10的远程桌面

    疫情严重,公司实行远程办公.自己只有mac电脑,苦于3个系统间跨平台建立远程桌面. 今天,终于尝试成功!特来记录,以防别人踩坑! Mac远程软件安装 Remote Desktop Manager软件非 ...

  5. Java实现 LeetCode 770 基本计算器 IV(暴力+分析题)

    770. 基本计算器 IV 给定一个表达式 expression 如 expression = "e + 8 - a + 5" 和一个求值映射,如 {"e": ...

  6. (Java实现) 美元汇率

    时空限制 1000ms/128MB 题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后 ...

  7. Java实现 LeetCode 532 数组中的K-diff数对(双指针,滑动窗口)

    532. 数组中的K-diff数对 给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对.这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数 ...

  8. Java实现 LeetCode 382 链表随机节点

    382. 链表随机节点 给定一个单链表,随机选择链表的一个节点,并返回相应的节点值.保证每个节点被选的概率一样. 进阶: 如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现? ...

  9. Java实现 蓝桥杯VIP 算法训练 最长字符串

    题目描述 字符串可是比赛经常出的问题,那么给大家出一个题, 输入五个字符串,输出5个字符串当中最长的字符串.每个字符串长度在100以内,且全为小写字母. 输入 无 输出 无 样例输入 one two ...

  10. Java实现 LeetCode 319 灯泡开关

    319. 灯泡开关 初始时有 n 个灯泡关闭. 第 1 轮,你打开所有的灯泡. 第 2 轮,每两个灯泡你关闭一次. 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭).第 i 轮,每 ...