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. idea创建maven项目慢的原因以及解决方案

    问题分析;在idea中maven项目所依赖的jar包,默认是从中央仓库直接下载jar包,不管jar包是否在本地仓库存在,所以导致idea创建maven项目速度慢,那么要解决这个问题,那么将idea设置 ...

  2. 百度云百度网盘VIP不限速破解版绿色版-实测可用

    百度云百度网盘不限速VIP破解版绿色版-下载地址:https://www.90pan.com/b1548999

  3. CSS3新增伪类有那些?

    p:first-of-type 选择属于其父元素的首个元素 p:last-of-type 选择属于其父元素的最后元素 p:only-of-type 选择属于其父元素唯一的元素 p:only-child ...

  4. Myeclipse新建工作空间配置

    之前跟着尚硅谷JavaWeb视频学习,现在总结一下Myeclipse新建工作空间配置 Windows按钮下的Preferences 1. General -->   Workspace --&g ...

  5. Linux、Android系统调用从上层到底层的调用路径浅析

    参考: https://blog.csdn.net/liuhangtiant/article/details/85149369 http://blog.sina.com.cn/s/blog_79433 ...

  6. ASP.NET MVC 数据传递 控制器向视图传递

    控制器向视图传递 MVC 控制器向视图传递传递主要分为单页面传递和全局页面传递 1.单页面传递主要是用 ViewData属性 和ViewBag属性 语法: 赋值: ViewData["名称& ...

  7. Java实现k个数乘(cheng)(自然数的k乘积问题)

    k个数乘(cheng) 题目描述 桐桐想把一个自然数N分解成K个大于l的自然数相乘的形式,要求这K个数按从小到大排列,而且除了第K个数之外,前面(K-l)个数是N分解出来的最小自然数.例如:N=24, ...

  8. Java实现 LeetCode 115 不同的子序列

    115. 不同的子序列 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字 ...

  9. java实现亲密数

    假设有 a.b 两个数,若 a 的所有因子之和等于 b,b 的所有因子之和等于 a, 并且 a 不等于 b,则称 a 和 b 是一对亲密数.如 284 和 220 就是一对亲密数. 分析: 若要找出 ...

  10. Maven发布Release到中心仓库历程记录(无个人域名)

    Maven发布Release到中心仓库历程记录(无个人域名) 前言 因为前段时间自己做了一个爬虫项目(地址),自己很希望分享到maven中心仓库上,感觉拥有自己的jar包令我兴奋,便开始了maven发 ...