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删除列表内容
Aug 04 Python
python Matplotlib画图之调整字体大小的示例
Nov 20 Python
Python使用Scrapy保存控制台信息到文本解析
Dec 27 Python
Python爬虫抓取代理IP并检验可用性的实例
May 07 Python
python多线程调用exit无法退出的解决方法
Feb 18 Python
Python中按键来获取指定的值
Mar 02 Python
Python 用turtle实现用正方形画圆的例子
Nov 21 Python
PyQt5多线程刷新界面防假死示例
Dec 13 Python
Python 元组拆包示例(Tuple Unpacking)
Dec 24 Python
python爬虫开发之使用python爬虫库requests,urllib与今日头条搜索功能爬取搜索内容实例
Mar 10 Python
Python ckeditor富文本编辑器代码实例解析
Jun 22 Python
python 两种方法删除空文件夹
Sep 29 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中基于ts与nts版本- vc6和vc9编译版本的区别详解
2013/04/26 PHP
PHP实现的sqlite数据库连接类
2014/12/12 PHP
Zend Framework动作助手Json用法实例分析
2016/03/05 PHP
NodeJS url验证(url-valid)的使用方法
2013/11/18 NodeJs
仿百度联盟对联广告实现代码
2014/08/30 Javascript
Javascript基础教程之变量
2015/01/18 Javascript
JavaScript判断字符长度、数字、Email、电话等常用判断函数分享
2015/04/01 Javascript
jQuery跨域问题解决方案
2015/08/03 Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
2017/01/11 Javascript
layui清空,重置表单数据的实例
2019/09/12 Javascript
javascript读取本地文件和目录方法详解
2020/08/06 Javascript
vue-router 2.0 跳转之router.push()用法说明
2020/08/12 Javascript
从表单校验看JavaScript策略模式的使用详解
2020/10/17 Javascript
python中Genarator函数用法分析
2015/04/08 Python
使用Python的Twisted框架构建非阻塞下载程序的实例教程
2016/05/25 Python
Python常见格式化字符串方法小结【百分号与format方法】
2016/09/18 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
2018/07/13 Python
Python实现正整数分解质因数操作示例
2018/08/01 Python
基于腾讯云服务器部署微信小程序后台服务(Python+Django)
2019/05/08 Python
Python中的类与类型示例详解
2019/07/10 Python
Pytorch反向求导更新网络参数的方法
2019/08/17 Python
Python实现的微信红包提醒功能示例
2019/08/22 Python
Keras官方中文文档:性能评估Metrices详解
2020/06/15 Python
利用python汇总统计多张Excel
2020/09/22 Python
记一次python 爬虫爬取深圳租房信息的过程及遇到的问题
2020/11/24 Python
HTML文本属性&amp;颜色控制属性的实现
2019/12/17 HTML / CSS
意大利在线眼镜精品店:Ottica Lipari
2019/11/11 全球购物
保险内勤岗位职责
2014/04/05 职场文书
公司总经理任命书
2014/06/05 职场文书
毕业证丢失证明范本
2014/09/20 职场文书
推普周国旗下讲话稿
2014/09/21 职场文书
申报优秀教师材料
2014/12/16 职场文书
电影开国大典观后感
2015/06/04 职场文书
学习经验交流会总结
2015/11/02 职场文书
golang import自定义包方式
2021/04/29 Golang
Windows10安装Apache2.4的方法步骤
2022/06/25 Servers