文章目录

  • 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).可迭代对象的特征:

  1. 字符串,列表,元祖,字典,集合、文件等等,都是可迭代对象
  2. 实现了__iter__方法的对象就叫做可迭代对象,_iter__方法可以返回一个迭代器对象,然后通过next()方法就能获取一个一个的元素。
  3. 直观理解就是, 能用for循环进行迭代的对象就是可迭代对象。

【如下图很重要、很重要、很重要!!!】


理解:

  1. 例如X列表对象,可以通过iter()方法获取到迭代器,通过迭代器的next()方法就能获取到对象元素,则说明X列表对象就是可迭代对象
  2. 中间有个新概念—迭代器,这个后续详解…

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.为什么要使用迭代器?

  1. 迭代器基本不占用内存资源和减少计算周期。
  2. 因为迭代器的计算是惰性的。可以理解为每次执行next()方法,每次才计算一次,否者不计算和保存数据。
  3. 迭代器还可以记录执行的进度,下次再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).生成器的特征?

  1. 生成器其实是一种特殊的迭代器,具备迭代器的性质, 不过这种迭代器更加优雅。
  2. 它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。
  3. 生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。

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

  1. 生成器与迭代器一样,当所有元素迭代完成之后,如果再执行next()函数,会报错。为了优化这个问题,可以使用return解决
  2. 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
  1. 通过以上send()函数的使用,说明send一次,相当于也next()一次,而且也传递了值给temp变量接收,说明同时做了2件事情。
  2. a.send(“hello”) 的结果,相当于是next(a)的结果
  3. 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)的理解和应用相关推荐

  1. python3可迭代对象、迭代器、生成器、协程yield入门

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2019-01-24 16:13:07 # @Author : cdl (1217096 ...

  2. python3迭代器和可迭代对象_一文读懂 Python3 可迭代对象、迭代器、生成器区别...

    笔者学习Python已有一段时间,一直以为对于可迭代对象(iterable).迭代器(iterator).生成器(generator)有一定理解了,直到看到<流畅的python>中的讲解才 ...

  3. 怎么确定迭代器后面还有至少两个值_如何理解Python中的可迭代对象、迭代器和生成器

    ▍前言 在讨论可迭代对象.迭代器和生成器之前,先说明一下迭代器模式(iterator pattern),维基百科这么解释: 迭代器是一种最简单也最常见的设计模式.它可以让用户透过特定的接口巡访容器中的 ...

  4. 完全理解 Python 迭代对象、迭代器、生成器(转)

    完全理解 Python 迭代对象.迭代器.生成器 本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators » nvie.com,俺写的这篇文章是 ...

  5. python 生成器 send_python(可迭代对象,迭代器,生成器及send方法详解)

    一.可迭代对象 对象必须提供一个__iter__()方法,如果有,那么就是可迭代对象, 像列表,元祖,字典等都是可迭代对象 可使用isinstance(obj,Iterable)方法判断 1 from ...

  6. 迭代对象、迭代器、生成器浅析

    迭代对象.迭代器.生成器浅析 这三者都是迭代对象,可通过for循环逐个获取对象元素.生成器基本不占用内存无论有多大数据量,但是只能使用一次(也可以通过一些途径使用多次). 迭代对象 iterables ...

  7. 完全理解Python迭代对象、迭代器、生成器

    本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators,俺写的这篇文章是按照自己的理解做的参考翻译,算不上是原文的中译版本,推荐阅读原文,谢谢网 ...

  8. 完全理解python迭代对象_完全理解Python迭代对象、迭代器、生成器

    1.assert:python assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假.可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触 ...

  9. python可迭代对象 迭代器生成器_Python可迭代对象、迭代器和生成器

    8.1 可迭代对象(Iterable) 大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的. __iter__方法会返回迭代器(iterator)本身,例如: >>&g ...

  10. python可迭代对象,迭代器,生成器

    容器是一系列元素的集合,str.list.set.dict.file.sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象. 可迭代对象实现 ...

最新文章

  1. HTML 框架 frameset,frame
  2. 电脑电池修复_笔记本电脑不充电是怎么回事?
  3. Androidstudio无法修改按钮颜色
  4. 在myeclipse中建立maven项目
  5. 文件句柄(file handles) 文件描述符(file descriptors)
  6. Linux vi编辑器常见命令的使用
  7. 2019蚂蚁金服 Java面试题目!涵盖现场3面真题
  8. 【ClickHouse】查看数据库容量和表大小的方法(system.parts各种操作方法)
  9. 打开SVN server图形化管理界面
  10. 山东大学软件学院概率论与数理统计(考试)——期末考试回忆版
  11. VUE引入JsBarcode组件异常记录
  12. 希科系统(CxServer)经济效益和社会效益分析
  13. 基于Python实现的机器人自动走迷宫
  14. pytorch学习笔记——2.4torch.nn模块简介
  15. 百度DNS/阿里DNS/114DNS/谷歌DNS/OpenDNS 对比评测
  16. 《数据结构》 李春葆 第一章-绪论
  17. matlab基础及应用 李国朝,Matlab基础与应用(李晓鹏)
  18. 【机器学习】如何成为当下合格的算法工程师
  19. Verilog基础知识(异步FIFO)
  20. 电工电子工艺实训考核装置QY-GY01C

热门文章

  1. 台灯学生用哪个牌子最好?精选学生专用台灯第一品牌
  2. 部署Squid代理服务器 —— 反向代理(acl 访问控制 、sarg 日志分析、 Squid反向代理) —— 再续前缘..
  3. 视频去水印哪个好用,怎么一键去掉水印
  4. 折腾U盘启动盘之USB-CD ROM
  5. putchar和getchar的用法
  6. iOS如何防止文件被备份到iCloud 和iTunes?
  7. 魔兽世界世界服务器无法响应,魔兽世界怀旧服世界服务器无法连接问题解决方案...
  8. 为什么改了css网页没有变化_同样升级「大轮毂」:为什么有的汽车油耗升高,有些却没有变化?...
  9. LQ 关于 关于信标灯系统价格问题的回复
  10. 如何多人配音?告诉你这几个实用的配音技巧