Python进阶:生成器 懒人版本的迭代器详解


Posted in Python onJune 29, 2019

从容器、可迭代对象谈起

所有的容器都是可迭代的(iterable),迭代器提供了一个next方法。iter()返回一个迭代器,通过next()函数可以实现遍历。

def is_iterable(param):
try: 
iter(param) 
return True
except TypeError:
return False
params = [
1234,
'1234',
[1, 2, 3, 4],
set([1, 2, 3, 4]),
{1:1, 2:2, 3:3, 4:4},
(1, 2, 3, 4)
]
for param in params:
print('{} is iterable? {}'.format(param, is_iterable(param)))
########## 输出 ##########
# 1234 is iterable? False
# 1234 is iterable? True
# [1, 2, 3, 4] is iterable? True
# {1, 2, 3, 4} is iterable? True
# {1: 1, 2: 2, 3: 3, 4: 4} is iterable? True
# (1, 2, 3, 4) is iterable? True

除了数字外,其他数据结构都是可迭代的。

生成器是什么

生成器是懒人版本的迭代器。例:

import os
import psutil

#显示当前 python 程序占用的内存大小
def show_memory_info(hint):
pid = os.getpid()
p = psutil.Process(pid)

info = p.memory_full_info()
memory = info.uss / 1024. / 1024
print('{} memory used: {} MB'.format(hint, memory))

def test_iterator():
show_memory_info('initing iterator')
list_1 = [i for i in range(100000000)]
show_memory_info('after iterator initiated')
print(sum(list_1))
show_memory_info('after sum called')

def test_generator():
show_memory_info('initing generator')
list_2 = (i for i in range(100000000))
show_memory_info('after generator initiated')
print(sum(list_2))
show_memory_info('after sum called')

test_iterator()
test_generator()
%time test_iterator()
%time test_generator()

######### 输出 ##########

initing iterator memory used: 48.9765625 MB
after iterator initiated memory used: 3920.30078125 MB
4999999950000000
after sum called memory used: 3920.3046875 MB
Wall time: 17 s
initing generator memory used: 50.359375 MB
after generator initiated memory used: 50.359375 MB
4999999950000000
after sum called memory used: 50.109375 MB
Wall time: 12.5 s

[i for i in range(100000000)] 声明了一个迭代器,每个元素在生成后都会保存到内存中,占用了巨量的内存。(i for i in range(100000000)) 初始化了一个生成器,可以看到,生成器并不会像迭代器一样占用大量的内存,相比于 test_iterator(),test_generator()函数节省了一次生成一亿个元素的过程。在调用next()的时候,才会生成下一个变量.

生成器能玩啥花样

数学中有一个恒等式,(1 + 2 + 3 + ... + n)^2 = 1^3 + 2^3 + 3^3 + ... + n^3,用以下代码表达

def generator(k):
i = 1
while True:
yield i ** k
i += 1

gen_1 = generator(1)
gen_3 = generator(3)
print(gen_1)
print(gen_3)

def get_sum(n):
sum_1, sum_3 = 0, 0
for i in range(n):
next_1 = next(gen_1)
next_3 = next(gen_3)
print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
sum_1 += next_1
sum_3 += next_3
print(sum_1 * sum_1, sum_3)

get_sum(8)

########## 输出 ##########

# <generator object generator at 0x000001E70651C4F8>
# <generator object generator at 0x000001E70651C390>
# next_1 = 1, next_3 = 1
# next_1 = 2, next_3 = 8
# next_1 = 3, next_3 = 27
# next_1 = 4, next_3 = 64
# next_1 = 5, next_3 = 125
# next_1 = 6, next_3 = 216
# next_1 = 7, next_3 = 343
# next_1 = 8, next_3 = 512
# 1296 1296

generator()这个函数,它返回了一个生成器,当运行到yield i ** k时,暂停并把i ** k作为next()的返回值。每次调用next(gen)时,暂停的程序会启动并往下执行,而且i的值也会被记住,继续累加,最后next_1为8,next_3为512.

仔细查看这个示例,发现迭代器是一个有限集合,生成器则可以成为一个无限集。调用next(),生成器根据运算会自动生成新的元素,然后返回给你,非常便捷。

再来看一个问题:给定一个list和一个指定数字,求这个数字在list中的位置:

#常规写法
def index_normal(L, target):
result = []
for i, num in enumerate(L):
if num == target:
result.append(i)
return result
print(index_normal([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2))
########## 输出 ##########
[2, 5, 9]
#生成器写法
def index_generator(L, target):
for i, num in enumerate(L):
if num == target:
yield i
print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 2)))
######### 输出 ##########
[2, 5, 9]

再看一例子:

查找子序列:给定两个字符串a,b,查找字符串a是否字符串b的子序列,所谓子序列,即一个序列包含在另一个序列中并且顺序一

算法:分别用两个指针指向两个字符串的头,然后往后移动找出相同的值,如果其中一个指针走完了整个字符串也没有相同的值,则不是子序列

def is_subsequence(a, b):
b = iter(b)
return all(i in b for i in a)
print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))
######### 输出 ##########
True
False

下面代码为上面代码的演化版本

def is_subsequence(a, b):
b = iter(b)
print(b)

gen = (i for i in a)
print(gen)

for i in gen:
print(i)

gen = ((i in b) for i in a)
print(gen)

for i in gen:
print(i)

return all(((i in b) for i in a))

print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))

########## 输出 ##########

# <list_iterator object at 0x000001E7063D0E80>
# <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
# 1
# 3
# 5
# <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
# True
# True
# True
# False
# <list_iterator object at 0x000001E7063D0D30>
# <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
# 1
# 4
# 3
# <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
# True
# True
# False
# False

首先iter(b)把b转为迭代器。目的是内部实现next函数,(i for i in a) 会产生一个生成器 ,同样((i in b) for i in a)也是。然后(i in b)等阶于:

while True:
val = next(b)
if val == i:
yield True

这里非常巧妙地利用生成器的特性,next()函数运行的时候,保存了当前的指针。比如下面这个示例

b = (i for i in range(5))
print(2 in b)
print(4 in b)
print(3 in b)
########## 输出 ##########
True
True
False

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python常用web框架简单性能测试结果分享(包含django、flask、bottle、tornado)
Aug 25 Python
python 将对象设置为可迭代的两种实现方法
Jan 21 Python
python和mysql交互操作实例详解【基于pymysql库】
Jun 04 Python
对python 中re.sub,replace(),strip()的区别详解
Jul 22 Python
PyTorch中Tensor的维度变换实现
Aug 18 Python
浅谈python之自动化运维(Paramiko)
Jan 31 Python
用什么库写 Python 命令行程序(示例代码详解)
Feb 20 Python
django model object序列化实例
Mar 13 Python
用python给csv里的数据排序的具体代码
Jul 17 Python
python excel多行合并的方法
Dec 09 Python
虚拟环境及venv和virtualenv的区别说明
Feb 05 Python
浅谈Python中对象是如何被调用的
Apr 06 Python
简单了解Python matplotlib线的属性
Jun 29 #Python
Jacobi迭代算法的Python实现详解
Jun 29 #Python
Gauss-Seidel迭代算法的Python实现详解
Jun 29 #Python
python异步实现定时任务和周期任务的方法
Jun 29 #Python
python循环定时中断执行某一段程序的实例
Jun 29 #Python
python顺序执行多个py文件的方法
Jun 29 #Python
如何使用python把ppt转换成pdf
Jun 29 #Python
You might like
php 删除无限级目录与文件代码共享
2008/11/22 PHP
PHP 杂谈《重构-改善既有代码的设计》之四 简化条件表达式
2012/04/09 PHP
PHP给文字内容中的关键字进行套红处理
2016/04/12 PHP
php分页查询的简单实现代码
2017/03/14 PHP
javascript new 需不需要继续使用
2009/07/02 Javascript
深入理解JavaScript系列(10) JavaScript核心(晋级高手必读篇)
2012/01/15 Javascript
JavaScript实现三阶幻方算法谜题解答
2014/12/29 Javascript
jQuery 处理页面的事件详解
2015/01/20 Javascript
jQuery实现标题有打字效果的焦点图代码
2015/11/16 Javascript
玩转JavaScript OOP - 类的实现详解
2016/06/08 Javascript
JS实现的文件拖拽上传功能示例
2018/05/21 Javascript
vue计算属性无法监听到数组内部变化的解决方案
2019/11/06 Javascript
node.js实现http服务器与浏览器之间的内容缓存操作示例
2020/02/11 Javascript
[03:46]DOTA2英雄基础教程 维萨吉
2013/12/11 DOTA
python的tkinter布局之简单的聊天窗口实现方法
2014/09/03 Python
python打开文件并获取文件相关属性的方法
2015/04/23 Python
Linux下通过python访问MySQL、Oracle、SQL Server数据库的方法
2016/04/23 Python
Python 将Matrix、Dict保存到文件的方法
2018/10/30 Python
Python实现的逻辑回归算法示例【附测试csv文件下载】
2018/12/28 Python
Django使用中间件解决前后端同源策略问题
2019/09/02 Python
Python魔法方法 容器部方法详解
2020/01/02 Python
利用Tensorflow构建和训练自己的CNN来做简单的验证码识别方式
2020/01/20 Python
Python面向对象魔法方法和单例模块代码实例
2020/03/25 Python
pandas读取csv文件提示不存在的解决方法及原因分析
2020/04/21 Python
中国综合网上购物商城:苏宁易购
2016/08/09 全球购物
美国著名的女性内衣零售商:Frederick’s of Hollywood
2018/02/24 全球购物
蔻驰法国官网:COACH法国
2018/11/14 全球购物
活动总结怎么写
2014/04/28 职场文书
机械制造专业大学生自我鉴定
2014/09/19 职场文书
2014学校领导四风问题对照检查材料思想汇报
2014/09/22 职场文书
大学生党员自我批评思想汇报
2014/10/10 职场文书
2015年圣诞节活动总结
2015/03/24 职场文书
nginx+lua单机上万并发的实现
2021/05/31 Servers
基于Python实现射击小游戏的制作
2022/04/06 Python
讲解MySQL增删改操作
2022/05/06 MySQL
Pygame游戏开发之太空射击实战敌人精灵篇
2022/08/05 Python