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 FTP操作类代码分享
May 13 Python
python操作mysql中文显示乱码的解决方法
Oct 11 Python
Python使用MYSQLDB实现从数据库中导出XML文件的方法
May 11 Python
Python实现Sqlite将字段当做索引进行查询的方法
Jul 21 Python
10 分钟快速入门 Python3的教程
Jan 29 Python
利用anaconda作为python的依赖库管理方法
Aug 13 Python
pycharm配置git(图文教程)
Aug 16 Python
Django单元测试中Fixtures的使用方法
Feb 26 Python
Windows10+anacond+GPU+pytorch安装详细过程
Mar 24 Python
使用python接受tgam的脑波数据实例
Apr 09 Python
Python提取视频中图片的示例(按帧、按秒)
Oct 22 Python
Python实现双向链表基本操作
May 25 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 AJAX实例根据邮编自动完成地址信息
2008/11/23 PHP
PHP不用递归实现无限分级的例子分享
2014/04/18 PHP
php实现的ping端口函数实例
2014/11/12 PHP
让你的PHP7更快之Hugepage用法分析
2016/05/31 PHP
PHP文件管理之实现网盘及压缩包的功能操作
2017/09/20 PHP
JavaScript实现禁止后退的方法
2006/12/27 Javascript
jQuery 第二课 操作包装集元素代码
2010/03/14 Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
2013/04/11 Javascript
ajax请求get与post的区别总结
2013/11/04 Javascript
js使用栈来实现10进制转8进制与取除数及余数
2014/06/11 Javascript
javascript学习笔记(一)基础知识
2014/09/30 Javascript
jquery实现select选择框内容左右移动代码分享
2015/11/21 Javascript
Jquery修改image的src属性,图片不加载问题的解决方法
2016/05/17 Javascript
Bootstrap表单布局
2016/07/19 Javascript
JS对HTML表格进行增删改操作
2016/08/22 Javascript
扩展Bootstrap Tooltip插件使其可交互的方法
2016/11/07 Javascript
Vue路由跳转问题记录详解
2017/06/15 Javascript
详解vuejs之v-for列表渲染
2017/06/22 Javascript
Vue精简版风格概述
2018/01/30 Javascript
JS使用H5实现图片预览功能
2019/09/30 Javascript
vue 单页应用和多页应用的优劣
2020/10/22 Javascript
浅谈vue在html中出现{{}}的原因及解决方式
2020/11/16 Javascript
详解React中共享组件逻辑的三种方式
2021/02/02 Javascript
Python实现的基数排序算法原理与用法实例分析
2017/11/23 Python
python命名空间(namespace)简单介绍
2019/08/10 Python
Python如何使用Gitlab API实现批量的合并分支
2019/11/27 Python
英国门销售网站:Green Tree Doors
2020/01/07 全球购物
Java语言程序设计测试题选择题部分
2014/04/03 面试题
经贸日语专业个人求职信
2013/12/13 职场文书
电子商务应届生自我鉴定
2014/01/13 职场文书
小学教师师德师风个人整改措施
2014/09/18 职场文书
团委副书记工作总结
2015/08/14 职场文书
2016学习依法治国心得体会
2016/01/15 职场文书
爱岗敬业先进典型事迹材料(2016推荐版)
2016/02/26 职场文书
MySQL 百万级数据的4种查询优化方式
2021/06/07 MySQL
Go语言实现一个简单的并发聊天室的项目实战
2022/03/18 Golang