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批量修改文件后缀示例代码分享
Dec 24 Python
跟老齐学Python之赋值,简单也不简单
Sep 24 Python
Python实现的简单发送邮件脚本分享
Nov 07 Python
用Python的Django框架来制作一个RSS阅读器
Jul 22 Python
Python使用Beautiful Soup包编写爬虫时的一些关键点
Jan 20 Python
分享Python开发中要注意的十个小贴士
Aug 30 Python
Python语言生成水仙花数代码示例
Dec 18 Python
python爬虫获取淘宝天猫商品详细参数
Jun 23 Python
Python发展史及网络爬虫
Jun 19 Python
python安装requests库的实例代码
Jun 25 Python
Kali Linux安装ipython2 和 ipython3的方法
Jul 11 Python
python数据可视化使用pyfinance分析证券收益示例详解
Nov 20 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+APACHE实现用户论证的方法
2006/10/09 PHP
PHP开发规范手册之PHP代码规范详解
2011/01/13 PHP
rephactor 优秀的PHP的重构工具
2011/06/09 PHP
PHP+Ajax异步通讯实现用户名邮箱验证是否已注册( 2种方法实现)
2011/12/28 PHP
php 带逗号千位符数字的处理方法
2012/01/10 PHP
php基于Snoopy解析网页html的方法
2015/07/09 PHP
基于PHP后台的Android新闻浏览客户端
2016/05/23 PHP
浅谈PHP中try{}catch{}的使用方法
2016/12/09 PHP
PHP vsprintf()函数格式化字符串操作原理解析
2020/07/14 PHP
为什么要在引入的css或者js文件后面加参数的详细讲解
2013/05/03 Javascript
JQuery操作三大控件(下拉,单选,复选)的方法
2013/08/06 Javascript
JS方法调用括号的问题探讨
2014/01/24 Javascript
Js保留小数点的4种效果实现代码分享
2014/04/12 Javascript
javascript实现的淘宝旅行通用日历组件用法实例
2015/08/03 Javascript
javascript时间排序算法实现活动秒杀倒计时效果
2021/01/28 Javascript
Vue.js 60分钟快速入门教程
2017/03/28 Javascript
NodeJs的fs读写删除移动监听
2017/04/28 NodeJs
基于react框架使用的一些细节要点的思考
2017/05/31 Javascript
浅谈JavaScript闭包
2019/04/09 Javascript
简单了解TypeScript中如何继承 Error 类
2019/06/21 Javascript
[37:45]完美世界DOTA2联赛PWL S3 LBZS vs Phoenix 第二场 12.09
2020/12/11 DOTA
python 实现判断ip连通性的方法总结
2018/04/22 Python
解决Python网页爬虫之中文乱码问题
2018/05/11 Python
Python发送邮件测试报告操作实例详解
2018/12/08 Python
python文件操作的简单方法总结
2019/11/07 Python
Python selenium使用autoIT上传附件过程详解
2020/05/26 Python
Python多线程的退出控制实现
2020/08/10 Python
Python timeit模块原理及使用方法
2020/10/10 Python
python复合条件下的字典排序
2020/12/18 Python
用python制作个音乐下载器
2021/01/30 Python
美国在线购买和出售礼品卡网站:EJ Gift Cards
2019/06/09 全球购物
思想专业自荐信范文
2013/12/25 职场文书
大学生创业感言
2014/01/25 职场文书
《诚实与信任》教学反思
2014/04/10 职场文书
2014年全国爱牙日宣传活动方案
2014/09/21 职场文书
导游词之日本富士山
2020/01/06 职场文书