Python3---可迭代对象(iterable)、迭代器(iterator)、生成器(generator)的理解和应用
文章目录
- 1. 可迭代对象(iterable)
- 1).可迭代性----for循环原理
- 2).可迭代对象的特征:
- 3).可迭代对象的源码:
- 2. 迭代器(iterator)
- 1).迭代器的源码:
- 2).可迭代对象 & 迭代器的区别
- 3).自定义迭代器---斐波那契数列
- 4).迭代器的应用场景?
- 3. 生成器(generator)
- 1).生成器的特征?
- 2).生成器的创建?
- 3).yield 的工作流程
- 4).生成器中的yield & return
- 5).生成器中的send()& next()区别
- 6).生成器的应用---简单的生产/消费模型
- 7).生成器的应用---yield多任务切换(协程模拟)
- 8).生成器的应用---大文件的读取
1. 可迭代对象(iterable)
1).可迭代性----for循环原理
1.字符串,列表,元祖,字典,集合、文件等等,都是可迭代对象,都具备可迭代特性。
2.具备可迭代性,不代表就是可迭代对象
1. 包含__getitem__魔法方法:具备可迭代性
from collections import Iterable# 1、只实现__getitem__
class A:def __init__(self):self.data = [1, 2, 3]def __getitem__(self, index):return self.data[index]a = A()
print(isinstance(a, Iterable)) # 判断是否为可迭代对象for i in a:print(i)# 结果:
False
1
2
3
2. 包含__getitem__魔法方法 & __iter__魔法方法: 可迭代对象
from collections import Iterableclass A:def __init__(self):self.data = [1, 2, 3]self.data1 = [4, 5, 6]def __iter__(self):return iter(self.data1)def __getitem__(self, index):return self.data[index]a = A()
print(isinstance(a, Iterable)) # 判断是否为可迭代对象
for i in a:print(i)# 结果为:
True
4
5
6
- 如以上代码所示,如果只是实现__getitem__,for in 循环体会自动调用__getitem__函数,并自动对Index从0开始自增,并将对应索引位置的值赋值给 i,直到引发IndexError错误
- 如果还实现了__iter__,则会忽略__getitem__,只调用__iter__,并对__iter__返回的迭代器进行成员遍历,并自动将遍历的成员逐次赋值给 i,直到引发StopIteration
2).可迭代对象的特征:
- 字符串,列表,元祖,字典,集合、文件等等,都是可迭代对象
- 实现了__iter__方法的对象就叫做可迭代对象,_iter__方法可以返回一个迭代器对象,然后通过next()方法就能获取一个一个的元素。
- 直观理解就是, 能用for循环进行迭代的对象就是可迭代对象。
【如下图很重要、很重要、很重要!!!】
理解:
- 例如X列表对象,可以通过iter()方法获取到迭代器,通过迭代器的next()方法就能获取到对象元素,则说明X列表对象就是可迭代对象
- 中间有个新概念—迭代器,这个后续详解…
my_list = ["hello", "alien", "world"]# 如下两种方法效果一样,都是获取到迭代器
list_type_iterator = my_list.__iter__() # 通过此方法,查看可迭代对象源码
# list_type_iterator = iter(my_list)print(list_type_iterator)
item = list_type_iterator.next() # 通过此方法,查看迭代器源码
print(item)
item = list_type_iterator.next()
print(item)
item = list_type_iterator.next()
print(item)
<listiterator object at 0x10a8fa3d0>
hello
alien
world
3).可迭代对象的源码:
# 通过如上代码中,通过__iter__()函数进入到源码内部(Ctrl + B),让我们一探究竟list_type_iterator = my_list.__iter__()进入到__builtin__.py文件中,这个文件定义了python3中常用的数据类型。
我们在此文件中搜索__iter__方法,发现代码如下情况的代码:
class list(object):"""list() -> new empty listlist(iterable) -> new list initialized from iterable's items"""def append(self, p_object): # real signature unknown; restored from __doc__""" L.append(object) -- append object to end """passdef __iter__(self): # real signature unknown; restored from __doc__""" x.__iter__() <==> iter(x) """pass
class dict(object):"""dict() -> new empty dictionarydict(mapping) -> new dictionary initialized from a mapping object's(key, value) pairsdict(iterable) -> new dictionary initialized as if via:d = {}for k, v in iterable:d[k] = vdict(**kwargs) -> new dictionary initialized with the name=value pairsin the keyword argument list. For example: dict(one=1, two=2)"""def clear(self): # real signature unknown; restored from __doc__""" D.clear() -> None. Remove all items from D. """passdef __iter__(self): # real signature unknown; restored from __doc__""" x.__iter__() <==> iter(x) """pass
class file(object):"""file(name[, mode[, buffering]]) -> file objectOpen a file. The mode can be 'r', 'w' or 'a' for reading (default),writing or appending. The file will be created if it doesn't existwhen opened for writing or appending; it will be truncated whenopened for writing. Add a 'b' to the mode for binary files.Add a '+' to the mode to allow simultaneous reading and writing.If the buffering argument is given, 0 means unbuffered, 1 means linebuffered, and larger numbers specify the buffer size. The preferred wayto open a file is with the builtin open() function.Add a 'U' to mode to open the file for input with universal newlinesupport. Any line ending in the input file will be seen as a '\n'in Python. Also, a file so opened gains the attribute 'newlines';the value for this attribute is one of None (no newline read yet),'\r', '\n', '\r\n' or a tuple containing all the newline types seen.'U' cannot be combined with 'w' or '+' mode."""def readline(self, size=None): # real signature unknown; restored from __doc__"""readline([size]) -> next line from the file, as a string.Retain newline. A non-negative size argument limits the maximumnumber of bytes to return (an incomplete line may be returned then).Return an empty string at EOF."""passdef close(self): # real signature unknown; restored from __doc__"""close() -> None or (perhaps) an integer. Close the file.Sets data attribute .closed to True. A closed file cannot be used forfurther I/O operations. close() may be called more than once withouterror. Some kinds of file objects (for example, opened by popen())may return an exit status upon closing."""passdef __iter__(self): # real signature unknown; restored from __doc__""" x.__iter__() <==> iter(x) """pass
- 我们发现常用的可迭代对象都定义了__iter__方法,所以后续我们自动以迭代器的时候,也需要这个方法。
2. 迭代器(iterator)
1).迭代器的源码:
# 通过文章最开始的 next()方法,可以进入到迭代器的源码中查看究竟item = list_type_iterator.next()item 即为可迭代对象中的一个元素
@runtime_checkable
class Iterable(Protocol[_T_co]):@abstractmethoddef __iter__(self) -> Iterator[_T_co]: ...# 解释:可迭代对象通过__iter__()方法得到迭代器@runtime_checkable
class Iterator(Iterable[_T_co], Protocol[_T_co]):@abstractmethoddef next(self) -> _T_co: ...# 解释:迭代器通过next()方法得到某个元素def __iter__(self) -> Iterator[_T_co]: ...
- 通过以上内容,发现迭代器中还有一个独特的方法—next(), 这个是为了获取其中一个元素
2).可迭代对象 & 迭代器的区别
通过如上的代码、源码我们得出结论:
- 可迭代对象都有一个__iter__函数
- 可迭代对象通过__iter__得到迭代器,迭代器再通过next()方法,可以得到其中的元素
- 每次next()之后,迭代器会记录当前执行的进度,下次执行next()的时候,继续上次的位置执行。这样每次迭代的时候元素是连续的。
3).自定义迭代器—斐波那契数列
class Fib():def __init__(self, max):self.n = 0self.prev = 0self.curr = 1self.max = maxdef __iter__(self):return selfdef __next__(self):if self.n < self.max:value = self.currself.curr += self.prevself.prev = valueself.n += 1return valueelse:raise StopIterationfb = Fib(5)
print(fb.__next__())
print(fb.__next__())
print(fb.__next__())
print(fb.__next__())
print(fb.__next__())
print(fb.__next__())
1
1
2
3
5Traceback (most recent call last):File "/Volumes/Develop/iterator_generator.py", line 43, in <module>print(fb.__next__())File "/Volumes/Develop/iterator_generator.py", line 34, in __next__raise StopIteration
StopIteration
注意:
1.在迭代器没有数据时, 如果再调用next()方法, 会抛出StopIteration错误
4).迭代器的应用场景?
1.为什么要使用迭代器?
- 迭代器基本不占用内存资源和减少计算周期。
- 因为迭代器的计算是惰性的。可以理解为每次执行next()方法,每次才计算一次,否者不计算和保存数据。
- 迭代器还可以记录执行的进度,下次再next()时候,可以从上次结束位置开始。
例如, 你想创建个有100000000个数据的斐波那契数列。如果全部生成之后再用,肯定会占用大量的内存资源。如果使用迭代器来处理,就基本可以忽略内存的占用和计算时间的成本。
2. 文件的读取用到迭代器原理
【普通读取方法】
# readlines()方法其实是读取文件所有内容并形成一个list,没一行内容是其中一个元素
for line in open("test.txt").readlines():print line# 1.把文件内容一次全部读取并加载到内存中,然后逐行打印。
# 2. 当文件很大时,这个方法的内存开销就很大了,如果文件大于内存的时候,程序会崩掉
【迭代器方式读取】
for line in open("test.txt"): #use file iteratorsprint line# 这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。
3. 生成器(generator)
1).生成器的特征?
- 生成器其实是一种特殊的迭代器,具备迭代器的性质, 不过这种迭代器更加优雅。
- 它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。
- 生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。
2).生成器的创建?
1. 只要把一个列表生成式的 [ ] 改成 ( )
L = [x * 2 for x in range(5)]
print(type(L))
G = (x * 2 for x in range(5))
print(G)# 结果如下:
<type 'list'>
<generator object <genexpr> at 0x10fa48730>#=================================# 获取生成器中的元素G = (x * 2 for x in range(5))print(next(G))
print(next(G))
print(next(G))# 结果如下
0
2
4
2. 用函数创建生成器
def fib(max):n, a, b = 0, 0, 1while n < max:yield ba, b = b, a + bn = n + 1a = fib(10)
print(next(a))
print(next(a))
print(next(a))# 结果:
1
1
2
3).yield 的工作流程
def fib(max):n, a, b = 0, 0, 1while n < max:print("yield--------start")yield b # 每次执行next()方法,都执行到这里,并返回一个元素a, b = b, a + b # yield下面的部分,下一次next方法再执行n = n + 1print("yield--------end")fb = fib(5)
print(next(fb))
print("\n")
print(next(fb))
print("\n")
print(next(fb))# 结果:
yield--------start
1yield--------end
yield--------start
1yield--------end
yield--------start
2
4).生成器中的yield & return
- 生成器与迭代器一样,当所有元素迭代完成之后,如果再执行next()函数,会报错。为了优化这个问题,可以使用return解决
- return可以在迭代完成,返回给特定的【错误信息】,然后通过try捕获StopIteration错误,即可接收到这个【错误信息】
def fib(max):n, a, b = 0, 0, 1while n < max:yield ba, b = b, a + bn = n + 1return 'iter num finish'
1.方式一:
def iter_list(iterator):try:x = next(iterator)print("----->", x)except StopIteration as ret:stop_reason = ret.valueprint(stop_reason)iter_list(fb)
iter_list(fb)
iter_list(fb)
iter_list(fb)
iter_list(fb)
iter_list(fb)# 结果:-----> 1
-----> 1
-----> 2
-----> 3
-----> 5
iter num finish
1.方式二:
fb = fib(5)def iter_list(iterator):while True:try:x = next(iterator)print("----->", x)except StopIteration as ret:stop_reason = ret.valueprint(stop_reason)breakiter_list(fb)# 结果:
-----> 1
-----> 1
-----> 2
-----> 3
-----> 5
iter num finish
5).生成器中的send()& next()区别
1.next()的作用是唤醒并继续执行
2.send()的作用是唤醒并继续执行,同时发送一个信息到生成器内部,需要一个变量接收
def fib(max):n, a, b = 0, 0, 1while n < max:temp = yield bprint("\n temp------>", temp)a, b = b, a + bn = n + 1a = fib(10)
print(next(a))abc = a.send("hello")
print(abc)abc = a.send("alien")
print(abc)# 结果:
1temp------> hello
1temp------> alien
2
- 通过以上send()函数的使用,说明send一次,相当于也next()一次,而且也传递了值给temp变量接收,说明同时做了2件事情。
- a.send(“hello”) 的结果,相当于是next(a)的结果
- send函数的执行,先将传递的值,赋值给temp,然后执行next的功能
6).生成器的应用—简单的生产/消费模型
def producter(num):print("produce %s product" % num)while num > 0:consume_num = yield numif consume_num:print("consume %s product" % consume_num)num -= consume_numelse:print("consume 1 time")num -= 1else:return "consume finish"p = producter(20)
print("start----->", next(p), "\n")
abc = p.send(2)
print("the rest num---->", abc, "\n")print("the rest num---->", next(p), "\n")# 结果:
produce 20 product
start-----> 20 consume 2 product
the rest num----> 18 consume 1 time
the rest num----> 17
7).生成器的应用—yield多任务切换(协程模拟)
协程的主要特点:
- 1.协程是非抢占式特点:协程也存在着切换,这种切换是由我们用户来控制的。协程主解决的是IO的操作
- 2.协程有极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显
- 3.协程无需关心多线程锁机制,也无需关心数据共享问题:不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多
def task1(times):for i in range(times):print('task_1 done the :{} time'.format(i + 1))yielddef task2(times):for i in range(times):print('task_2 done the :{} time'.format(i + 1))yieldgene1 = task1(5)
gene2 = task2(5)
for i in range(10):next(gene1)next(gene2)# 结果:
task_1 done the :1 time
task_2 done the :1 time
task_1 done the :2 time
task_2 done the :2 time
task_1 done the :3 time
task_2 done the :3 time
task_1 done the :4 time
task_2 done the :4 time
task_1 done the :5 time
task_2 done the :5 time
8).生成器的应用—大文件的读取
使用生成器的挂起并可重新在挂起点运行的特点,我么可以实现按需,每次读取指定大小的文件,避免因为读取文件时,因为一次性读取内容过多,导致内存溢出等问题
def read_file(fpath): BLOCK_SIZE = 1024 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return
Python3---可迭代对象(iterable)、迭代器(iterator)、生成器(generator)的理解和应用相关推荐
- python3可迭代对象、迭代器、生成器、协程yield入门
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2019-01-24 16:13:07 # @Author : cdl (1217096 ...
- python3迭代器和可迭代对象_一文读懂 Python3 可迭代对象、迭代器、生成器区别...
笔者学习Python已有一段时间,一直以为对于可迭代对象(iterable).迭代器(iterator).生成器(generator)有一定理解了,直到看到<流畅的python>中的讲解才 ...
- 怎么确定迭代器后面还有至少两个值_如何理解Python中的可迭代对象、迭代器和生成器
▍前言 在讨论可迭代对象.迭代器和生成器之前,先说明一下迭代器模式(iterator pattern),维基百科这么解释: 迭代器是一种最简单也最常见的设计模式.它可以让用户透过特定的接口巡访容器中的 ...
- 完全理解 Python 迭代对象、迭代器、生成器(转)
完全理解 Python 迭代对象.迭代器.生成器 本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators » nvie.com,俺写的这篇文章是 ...
- python 生成器 send_python(可迭代对象,迭代器,生成器及send方法详解)
一.可迭代对象 对象必须提供一个__iter__()方法,如果有,那么就是可迭代对象, 像列表,元祖,字典等都是可迭代对象 可使用isinstance(obj,Iterable)方法判断 1 from ...
- 迭代对象、迭代器、生成器浅析
迭代对象.迭代器.生成器浅析 这三者都是迭代对象,可通过for循环逐个获取对象元素.生成器基本不占用内存无论有多大数据量,但是只能使用一次(也可以通过一些途径使用多次). 迭代对象 iterables ...
- 完全理解Python迭代对象、迭代器、生成器
本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators,俺写的这篇文章是按照自己的理解做的参考翻译,算不上是原文的中译版本,推荐阅读原文,谢谢网 ...
- 完全理解python迭代对象_完全理解Python迭代对象、迭代器、生成器
1.assert:python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假.可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触 ...
- python可迭代对象 迭代器生成器_Python可迭代对象、迭代器和生成器
8.1 可迭代对象(Iterable) 大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的. __iter__方法会返回迭代器(iterator)本身,例如: >>&g ...
- python可迭代对象,迭代器,生成器
容器是一系列元素的集合,str.list.set.dict.file.sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象. 可迭代对象实现 ...
最新文章
- HTML 框架 frameset,frame
- 电脑电池修复_笔记本电脑不充电是怎么回事?
- Androidstudio无法修改按钮颜色
- 在myeclipse中建立maven项目
- 文件句柄(file handles) 文件描述符(file descriptors)
- Linux vi编辑器常见命令的使用
- 2019蚂蚁金服 Java面试题目!涵盖现场3面真题
- 【ClickHouse】查看数据库容量和表大小的方法(system.parts各种操作方法)
- 打开SVN server图形化管理界面
- 山东大学软件学院概率论与数理统计(考试)——期末考试回忆版
- VUE引入JsBarcode组件异常记录
- 希科系统(CxServer)经济效益和社会效益分析
- 基于Python实现的机器人自动走迷宫
- pytorch学习笔记——2.4torch.nn模块简介
- 百度DNS/阿里DNS/114DNS/谷歌DNS/OpenDNS 对比评测
- 《数据结构》 李春葆 第一章-绪论
- matlab基础及应用 李国朝,Matlab基础与应用(李晓鹏)
- 【机器学习】如何成为当下合格的算法工程师
- Verilog基础知识(异步FIFO)
- 电工电子工艺实训考核装置QY-GY01C
热门文章
- 台灯学生用哪个牌子最好?精选学生专用台灯第一品牌
- 部署Squid代理服务器 —— 反向代理(acl 访问控制 、sarg 日志分析、 Squid反向代理) —— 再续前缘..
- 视频去水印哪个好用,怎么一键去掉水印
- 折腾U盘启动盘之USB-CD ROM
- putchar和getchar的用法
- iOS如何防止文件被备份到iCloud 和iTunes?
- 魔兽世界世界服务器无法响应,魔兽世界怀旧服世界服务器无法连接问题解决方案...
- 为什么改了css网页没有变化_同样升级「大轮毂」:为什么有的汽车油耗升高,有些却没有变化?...
- LQ 关于 关于信标灯系统价格问题的回复
- 如何多人配音?告诉你这几个实用的配音技巧