Python三大器之迭代器
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:关于什么叫做经过优化的可迭代对象。会在结束迭代器和生成器的学习后专门开辟一章节来讲。
五大基本容器数据类型(
list,tuple,dict,set,str)等都是属于可迭代对象。本身并不属于迭代器。文件句柄对象本身是属于迭代器。
此外,
range()方法产生的数据类型是属于经过优化的可迭代对象。 以及,dict的keys,items,values等方法产生的数据类型也是属于经过优化的可迭代对象。
循环与迭代器
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.为序列和非序列类型提供了一种统一的迭代取值方式。(序列就是有序的,支持索引的数据类型,非序列是无序的,不支持索引的数据类型,如
dict,set等等)2.惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用
__next__来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。3.Python中为了能让一个容器类型能被多次取值。干脆将他们全部都做成可迭代对象,当对其调用
for循环时会自动创建迭代器,每一次for循环都创建出一个新的迭代器。
缺点
1、除非取尽,否则无法获取迭代器的长度
2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行
__next__方法直到值取尽,否则就会停留在某个位置,等待下一次调用__next__;若是要再次迭代同个对象,你只能重新调用__iter__方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。3、Python中的容器类型本身就占据一定的内存空间,如:
[1,2,3]。对其转换为迭代器只是为了方便多次被for循环调用,所以对可迭代对象每次for循环时创建出迭代器这种做法并不会节省很多内存。
疑点 - 如何自定义迭代器
我们目前创造迭代器的方式都是使用可迭代对象的
__iter__方法,那么这种创建迭代器的方式无疑是脱裤子放屁。
诸如
list,tuple,dict,set,dict等可迭代对象本身没经历任何优化,他们的存值占据了内存空间。迭代器本身具有惰性求值的特点,目的就是为了节省内存,但是对上述可迭代对象创建专属迭代器只是为了能够让其被多次调用。
我们怎么样才能自己创建一个迭代器,而不是通过上述的可迭代对象调用其
__iter__方法再创建一个迭代器呢?答案在下一章的生成器中。
Python三大器之迭代器的更多相关文章
- Python三大器之生成器
Python三大器之生成器 生成器初识 什么是生成器 生成器本身属于迭代器.继承了迭代器的特性,惰性求值,占用内存空间极小. 为什么要有生成器 我们想使用迭代器本身惰性求值的特点创建出一个可以容纳百万 ...
- Python三大器之装饰器
Python三大器之装饰器 开放封闭原则 一个良好的项目必定是遵守了开放封闭原则的,就比如一段好的Python代码必定是遵循PEP8规范一样.那么什么是开放封闭原则?具体表现在那些点? 开放封闭原则的 ...
- python三大器之while,if,for循环
一.for循环(遍历循环) 在Python你可能要经常遍历列表的所有元素,对每个元素执行相同的操作;对于包含数字的列表,可能要对每个元素进行相同的计算;在网站中,可能需要显示文章中的每个标题等等.某一 ...
- python基础(补充):python三大器之生成器
生成器的定义 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后 ...
- python基础(补充):python三大器之装饰器
函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): i = ...
- python三大器之装饰器的练习
装饰器 加载顺序从下至上 执行顺序从上至下 ''' 多层装饰器 ''' def deco1(func): #func=deco2 def wrapper1(*args, **kwargs): '''t ...
- python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器。
python三大神器之virtualenv pip, virtualenv, fabric通称为pythoner的三大神器. virtualenv virtualenv------用来建立一个虚拟 ...
- python基础之迭代器协议和生成器
迭代器和生成器补充:http://www.cnblogs.com/luchuangao/p/6847081.html 一 递归和迭代 略 二 什么是迭代器协议 1.迭代器协议是指:对象必须提供一个ne ...
- python基础之迭代器协议和生成器(一)
一 递归和迭代 二 什么是迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前 ...
随机推荐
- JAVA自学笔记(3)
JAVA的心动之旅 Day1 字符串String 1.0 字符串的特点以及创建一个字符串 public class Practice {//构建字符串的3+1种方法 public static voi ...
- Java并发编程 (三) 项目准备
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.案例环境初始化 1.环境搭建与准备 Spring Boot 项目,https://start.spr ...
- java实现报数游戏
报数游戏 有n个孩子站成一圈,从第一个孩子开始顺时针方向报数,报到3的人出列,下一个人继续从1报数,直到最后剩下一个孩子为止.问剩下第几个孩子.下面的程序以10个孩子为例,模拟了这个过程,请完善之(提 ...
- 逐行解读HashMap源码
[本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 一.写在前面 相 ...
- [原创][开源] SunnyUI.Net 更新日志
SunnyUI.Net, 基于 C# .Net WinForm 开源控件库.工具类库.扩展类库.多页面开发框架 Blog: https://www.cnblogs.com/yhuse Gitee: h ...
- Flask 四剑客
Flask 四剑客 返回字符串,返回 html , 跳转路由,返回 json from flask import Flask, render_template, redirect, jsonify a ...
- Spark读取Hbase中的数据
大家可能都知道很熟悉Spark的两种常见的数据读取方式(存放到RDD中):(1).调用parallelize函数直接从集合中获取数据,并存入RDD中:Java版本如下: JavaRDD<Inte ...
- (八)跑完用例后通过maven发送邮件
邮件类: package config; import com.sun.mail.util.MailSSLSocketFactory; import org.apache.log4j.Logger; ...
- java中工厂模式
最近在项目中使用了工厂模式来重构下之前的代码,在这里做个小结. 工厂模式最主要的特点是每次新增一个产品的时候,都需要新增一个新的工厂,这样在对于新的产品做扩展的时候,减少对客户端代码的修改. 我在项目 ...
- 《Java并发编程的艺术》第5章 Java中的锁 ——学习笔记
参考https://www.cnblogs.com/lilinzhiyu/p/8125195.html 5.1 Lock接口 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同 ...