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写的创建文件夹自定义函数mkdir()
Aug 25 Python
跟老齐学Python之折腾一下目录
Oct 24 Python
Python彩色化Linux的命令行终端界面的代码实例分享
Jul 02 Python
SVM基本概念及Python实现代码
Dec 27 Python
用tensorflow构建线性回归模型的示例代码
Mar 05 Python
Python设计模式之组合模式原理与用法实例分析
Jan 11 Python
Python使用统计函数绘制简单图形实例代码
May 15 Python
Django urls.py重构及参数传递详解
Jul 23 Python
Python中bisect的使用方法
Dec 31 Python
tensorflow将图片保存为tfrecord和tfrecord的读取方式
Feb 17 Python
python和pywin32实现窗口查找、遍历和点击的示例代码
Apr 01 Python
python 实现倒计时功能(gui界面)
Nov 11 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即时捕捉PHP中的错误并发送email通知的实现代码
2013/01/19 PHP
去除php注释和去除空格函数分享
2014/03/13 PHP
win7安装php框架Yii的方法
2016/01/25 PHP
利用PHP获取访客IP、地区位置、浏览器及来源页面等信息
2017/06/27 PHP
PHP错误处理函数register_shutdown_function使用示例
2017/07/03 PHP
PHP创建文件及写入数据(覆盖写入,追加写入)的方法详解
2019/02/15 PHP
JavaScript入门教程(6) Window窗口对象
2009/01/31 Javascript
讨论javascript(一)工厂方式 js面象对象的定义方法
2009/12/15 Javascript
改善你的jQuery的25个步骤 千倍级效率提升
2010/02/11 Javascript
复制js对象方法(详解)
2013/07/08 Javascript
JavaScript 模块化编程(笔记)
2015/04/08 Javascript
jQuery检测输入的字符串包含的中英文的数量
2015/04/17 Javascript
JavaScript面向对象编写购物车功能
2016/08/19 Javascript
Bootstrap modal 多弹窗之叠加显示不出弹窗问题的解决方案
2017/02/23 Javascript
基于js中的原型(全面讲解)
2017/09/19 Javascript
Vue多系统切换实现方案
2018/06/05 Javascript
如何在Vue中使用CleaveJS格式化你的输入内容
2018/12/14 Javascript
jQuery实现简单QQ聊天框
2020/08/27 jQuery
全面分析Python的优点和缺点
2018/02/07 Python
Python实现通讯录功能
2018/02/22 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
使用python3 实现插入数据到mysql
2020/03/02 Python
浅谈Python中threading join和setDaemon用法及区别说明
2020/05/02 Python
python输出结果刷新及进度条的实现操作
2020/07/13 Python
python模拟点击玩游戏的实例讲解
2020/11/26 Python
Selenium Webdriver元素定位的八种常用方式(小结)
2021/01/13 Python
英国著名的茶叶品牌:Whittard of Chelsea
2016/09/22 全球购物
波比布朗英国官网:Bobbi Brown英国
2017/11/13 全球购物
台湾7-ELEVEN线上购物中心:7-11
2021/01/21 全球购物
2014年社区植树节活动方案
2014/02/28 职场文书
企业道德讲堂实施方案
2014/03/19 职场文书
奠基仪式主持词
2014/03/20 职场文书
师德师风自我评价范文
2014/09/11 职场文书
2015银行年终工作总结范文
2015/05/26 职场文书
实习感想范文
2015/08/10 职场文书
tensorflow中的数据类型dtype用法说明
2021/05/26 Python