python3使用迭代生成器实现减少内存占用

技术背景

在python编码中for循环处理任务时,会将所有的待遍历参量加载到内存中。其实这本没有必要,因为这些参量很有可能是一次性使用的,甚至很多场景下这些参量是不需要同时存储在内存中的,这时候就会用到本文所介绍的迭代生成器yield。

基本使用

首先我们用一个例子来演示一下迭代生成器yield的基本使用方法,这个例子的作用是构造一个函数用于生成一个平方数组。在普通的场景中我们一般会直接构造一个空的列表,然后将每一个计算结果填充到列表中,最后return列表即可,对应的是这里的函数square_number。而另外一个函数square_number_yield则是为了演示yield而构造的函数,其使用语法跟return是一样的,不同的是每次只会返回一个值:

# test_yield.py

def square_number(length):
    s = []
    for i in range(length):
        s.append(i ** 2)
    return s

def square_number_yield(length):
    for i in range(length):
        yield i ** 2

if __name__ == '__main__':
    length = 10
    sn1 = square_number(length)
    sn2 = square_number_yield(length)
    for i in range(length):
        print (sn1[i], '\t', end='')
        print (next(sn2))

在main函数中我们对比了两种方法执行的结果,打印在同一行上面,用end=''指令可以替代行末的换行符号,具体执行的结果如下所示:

[dechin@dechin-manjaro yield]$ python3 test_yield.py
0       0
1       1
4       4
9       9
16      16
25      25
36      36
49      49
64      64
81      81

可以看到两种方法打印出来的结果是一样的。也许有些场景下就是需要持久化的存储函数中返回的结果,这一点用yield也是可以实现的,可以参考如下示例:

# test_yield.py

def square_number(length):
    s = []
    for i in range(length):
        s.append(i ** 2)
    return s

def square_number_yield(length):
    for i in range(length):
        yield i ** 2

if __name__ == '__main__':
    length = 10
    sn1 = square_number(length)
    sn2 = square_number_yield(length)
    sn3 = list(square_number_yield(length))
    for i in range(length):
        print (sn1[i], '\t', end='')
        print (next(sn2), '\t', end='')
        print (sn3[i])

这里使用的方法是直接将yield生成的对象转化成list格式,或者用sn3 = [i for i in square_number_yield(length)]这种写法也是可以的,在性能上应该差异不大。上述代码的执行结果如下:

[dechin@dechin-manjaro yield]$ python3 test_yield.py
0       0       0
1       1       1
4       4       4
9       9       9
16      16      16
25      25      25
36      36      36
49      49      49
64      64      64
81      81      81

进阶测试

在前面的章节中我们提到,使用yield可以节省程序的内存占用,这里我们来测试一个100000大小的随机数组的平方和计算。如果使用正常的逻辑,那么写出来的程序就是如下所示(关于python内存占用的追踪方法,可以参考这一篇博客):

# square_sum.py

import tracemalloc
import time
import numpy as np
tracemalloc.start()

start_time = time.time()
ss_list = np.random.randn(100000)
s = 0
for ss in ss_list:
    s += ss ** 2
end_time = time.time()
print ('Time cost is: {}s'.format(end_time - start_time))

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

for stat in top_stats[:5]:
    print (stat)

这个程序一方面通过time来测试执行的时间,另一方面利用tracemalloc追踪程序的内存变化。这里是先用np.random.randn()直接产生了100000个随机数的数组用于计算,那么自然在计算的过程中需要存储这些生成的随机数,就会占用这么多的内存空间。如果使用yield的方法,每次只产生一个用于计算的随机数,并且按照上一个章节中的用法,这个迭代生成的随机数也是可以转化为一个完整的list的:

# yield_square_sum.py

import tracemalloc
import time
import numpy as np
tracemalloc.start()

start_time = time.time()
def ss_list(length):
    for i in range(length):
        yield np.random.random()

s = 0
ss = ss_list(100000)
for i in range(100000):
    s += next(ss) ** 2
end_time = time.time()
print ('Time cost is: {}s'.format(end_time - start_time))

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

for stat in top_stats[:5]:
    print (stat)

这两个示例的执行结果如下,可以放在一起进行对比:

[dechin@dechin-manjaro yield]$ python3 square_sum.py
Time cost is: 0.24723434448242188s
square_sum.py:9: size=781 KiB, count=2, average=391 KiB
square_sum.py:12: size=24 B, count=1, average=24 B
square_sum.py:11: size=24 B, count=1, average=24 B
[dechin@dechin-manjaro yield]$ python3 yield_square_sum.py
Time cost is: 0.23023390769958496s
yield_square_sum.py:9: size=136 B, count=1, average=136 B
yield_square_sum.py:14: size=112 B, count=1, average=112 B
yield_square_sum.py:11: size=79 B, count=2, average=40 B
yield_square_sum.py:10: size=76 B, count=2, average=38 B
yield_square_sum.py:15: size=28 B, count=1, average=28 B

经过比较我们发现,两种方法的计算时间是几乎差不多的,但是在内存占用上yield有着明显的优势。当然,也许这个例子并不是非常的恰当,但是本文主要还是介绍yield的使用方法及其应用场景。

无限长迭代器

在参考链接1中提到了一种用法是无限长的迭代器,比如按顺序返回所有的素数,那么此时我们如果用return来返回所有的元素并存储到一个列表里面,就是一个非常不经济的办法,所以可以使用yield来迭代生成,参考链接1中的源代码如下所示:

def get_primes(number):
    while True:
        if is_prime(number):
            yield number
        number += 1

那么类似的,这里我们用while True可以展示一个简单的案例——返回所有的偶数:

# yield_iter.py

def yield_range2(i):
    while True:
        yield i
        i += 2

iter = yield_range2(0)
for i in range(10):
    print (next(iter))

因为这里我们限制了长度是10,所以最终会返回10个偶数:

[dechin@dechin-manjaro yield]$ python3 yield_iter.py

总结概要

本文介绍了python的迭代器yield,其实关于yield,我们可以简单的将其理解为单个元素的return。这样不仅就初步理解了yield的使用语法,也能够大概了解到yield的优势,也就是在计算过程中每次只占用一个元素的内存,而不需要一直存储大量的元素在内存中。

到此这篇关于python3使用迭代生成器实现减少内存占用的文章就介绍到这了,更多相关python3实现减少内存占用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python自动化之如何利用allure生成测试报告

    Allure测试报告框架帮助你轻松实现"高大上"报告展示.本文通过示例演示如何从0到1集成Allure测试框架.重点展示了如何将Allure集成到已有的自动化测试工程中.以及如何实现报表的优化展示.Allure非常强大,支持多种语言多种测试框架,无论是Java/Python还是Junit/TestNG,其他语言或者框架实现的流程和本文一致,具体配置参照各语言框架规范 安装 安装allure Windows用户: scoop install allure    (需要先下载并安装Scoo

  • python生成随机数、随机字符、随机字符串的方法示例

    本文是基于Windows 10系统环境,实现python生成随机数.随机字符.随机字符串: Windows 10 PyCharm 2018.3.5 for Windows (exe) python 3.6.8 Windows x86 executable installer (1) 生成随机数 随机整数 import random num = random.randint(1, 50) # 闭区间 print(num) 随机选取0到100间的偶数 import random num = rand

  • python opencv 找出图像中的最大轮廓并填充(生成mask)

    本文主要介绍了python opencv 找出图像中的最大轮廓并填充,分享给大家,具体如下: import cv2 import numpy as np from PIL import Image from joblib import Parallel from joblib import delayed # Parallel 和 delayed是为了使用多线程处理 # 使用前需要安装joblib:pip install joblib # img_stack的shape为:num, h, w #

  • python学习之可迭代对象、迭代器、生成器

    Iterable – 可迭代对象 能够逐一返回其成员项的对象. 可迭代对象的例子包括所有序列类型 (例如 list, str 和 tuple) 以及某些非序列类型例如 dict, 文件对象以及定义了__iter__()方法或是实现了序列语义的__getitem__() 方法的任意自定义类对象. 可迭代对象可用于 for 循环以及许多其他需要一个序列的地方(zip().map() -).当一个可迭代对象作为参数传给内置函数 iter() 时,它会返回该对象的迭代器.这种迭代器适用于对值集合的一次性

  • Python .py生成.pyd文件并打包.exe 的注意事项说明

    最近用python写了一个小程序,想发布出去让人试用又不想暴露源码,搜索了一下发现将py文件编译成pyd文件就能达到目的. 转换过程很简单,但是在调用pyd文件并且打包为单个exe文件的时候遇到一个坑,搞了一天才解决,在这里分享一下. 首先安装cython库 个人比较喜欢用清华的镜像库,速度快. pip install Cyphton -i https://pypi.tuna.tsinghua.edu.cn/simple 然后创建一个setup.py文件 写入以下内容: from distuti

  • python 生成xml文件,以及美化的实例代码

    看代码吧~ # -*- coding:utf-8 -*- import os import json import numpy as np #from xml.etree import ElementTree as etree from xml.etree.ElementTree import Element from xml.etree.ElementTree import SubElement from xml.etree.ElementTree import ElementTree ima

  • python基于opencv批量生成验证码的示例

    基本思路是使用opencv来把随机生成的字符,和随机生成的线段,放到一个随机生成的图像中去. 虽然没有加复杂的形态学处理,但是目前看起来效果还不错 尝试生成1000张图片,但是最后只有998张,因为有有重复的,被覆盖掉了. 代码如下: import cv2 import numpy as np line_num = 10 pic_num = 1000 path = "./imgs/" def randcolor(): return (np.random.randint(0,255),n

  • python实现由数组生成对称矩阵

    在实际应用中,经常会遇上这样的小需求:根据一段给定的数组,生成由这一段数组值构成的对称矩阵. 例如,给定数组[1,2,3,4,5,6,7,8,9,10],要求生成如下的矩阵: [[0,1,2,3,4], [1,0,5,6,7], [2,5,0,8,9], [3,6,8,0,10], [4,7,9,10,0]] 其中,对角元全为0,该类型的矩阵完全由给定的数组决定. 笔者给出实现以上功能的一种python参考代码如下: def semi_to_full(m): import numpy as np

  • python2利用wxpython生成投影界面工具的图文详解

    本投影界面工具的功能: 准备好.prj投影文件,将输入文件夹内的WGS84经纬度坐标shp文件,投影为平面文件,成果自动命名为prj_***并新建在输入文件夹同一路径下. 下一步目标: 利用pyinstaller或其他打包库生成exe文件,目前停滞在python2语法.arcpy打包出错相关问题上. 参考文献: <Using Py2exe with Arcpy- It can be done easily!> <如何使用py2exe打包arcpy脚本?> GUI界面示意图 投影文件

  • python实战之用emoji表情生成文字

    一.前言 昨夜刷b站的时候,看到了一条评论,形式如下图,于是心血来潮写了个python脚本,可以根据文字来生成这种由emoji拼接成的"文字". 二.思路 代码的思路极其简单,这种形式的"文字"可以理解为一张黑白图片,图中的白色使用一种emoji表情代替,黑色使用另一种emoji代替.那么想要生成这种"文字",则我们只需要两步即可,第一步是根据输入文字得到一张图,第二步则是遍历这张图片,当前像素的颜色为黑色时我们输出

  • 用Python生成N层的杨辉三角的实现方法

    [杨辉三角简介]   杨辉三角,是二项式系数在三角形中的一种几何排列.在欧洲,这个表叫做帕斯卡三角形. [代码实现] n = eval(input("请问你想生成几层的杨辉三角呢?")) result= [] def fun(N): # 杨辉三角生成函数 if N == 1: result.append([1]) elif N == 2: result.append([1]) result.append([1,1]) else: result.append([1]) result.ap

  • 用python自动生成日历

    效果 在Excel日历模板的基础上,生成带有农历日期.节假日.休班等信息的日历,解决DIY日历最大的技术难题. 图中日期,第一行为公历,第二行为节假日,第三行为农历,第四行是其他特别的日子,比如生日.纪念日等. 特点 使用门槛低 Python + Excel,会运行Python脚本,会使用Excel即可上手. 步骤简单 只需要修改Excel的年份(在一月份表头修改),运行一次脚本 可扩展 可制作任意年份的日历(修改年份即可) 可定制 可以添加其他特殊日期 使用手册 第一步,修改日历年份及样式 打

  • Python生成九宫格图片的示例代码

    一.前言 大家在朋友圈应该看到过用一张图片以九宫格的方式显示,效果大致如下: 要实现上面的效果非常简单,我们只需要截取图片的九个区域即可.今天我们就要带大家使用Python来实现一下九宫格图片的生成.在开始之前,我们需要安装一下Pillow模块,语句如下: pip install pillow 下面我们先来看看一些简单的图片操作. 二.图片基本操作 今天我们会使用到三个操作,分别是读取图片.保存图片和截取图片.下面我们分别来看看. 2.1 读取图片 在Pillow中,我们最常用的就是Image子

  • Python实现K-means聚类算法并可视化生成动图步骤详解

    K-means算法介绍 简单来说,K-means算法是一种无监督算法,不需要事先对数据集打上标签,即ground-truth,也可以对数据集进行分类,并且可以指定类别数目 牧师-村民模型 K-means 有一个著名的解释:牧师-村民模型: 有四个牧师去郊区布道,一开始牧师们随意选了几个布道点,并且把这几个布道点的情况公告给了郊区所有的村民,于是每个村民到离自己家最近的布道点去听课. 听课之后,大家觉得距离太远了,于是每个牧师统计了一下自己的课上所有的村民的地址,搬到了所有地址的中心地带,并且在海

随机推荐