Generator 生成器|学习笔记

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 快速学习 Generator 生成器

开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第二阶段:Generator 生成器】学习笔记,与课程紧密联系,让用户快速学习知识。  

课程地址:https://developer.aliyun.com/learning/course/712/detail/12717


Generator 生成器


内容介绍:

一、目标

二、概念

三、步骤

四、示例

五、小结


一、目标

了解生成器的作用和语法,掌握生成器对于内存的优化


二、概念

如果要生成一个复杂的数据,取出或遍历,需要先将其变为二维数组保存,再将二维数组进行 foreach 遍历。但是生成大量的数组需要大量时间并占用大量内存空间,此时对服务器的运行有明显的效率降低。生成器是一种使代码的使用方式变得简单,使内存优化的方法。

生成器: Generator,生成器提供了一种更容易的方法来实现简单的对象迭代

1.相比较定义类实现 lterator 接口的方式,性能开销和复杂性大大降低

2.生成器是一个类 Generator 实现了 lterator 接口,并且实现了 lterator 方法(修改了内部逻辑)

3.生成器是暂停循环执行逻辑,等到使用到的时候才触发循环再次执行。比如一个一万次的循环,当使用了生成器之后执行第一次就停,除非有第二次的输出需求才会执行第二次,一次只取一个,不像数组一次性把一万个全部取到数组里。逻辑是有一个一万次的循环,每次需要把 i 放到数组里存放,此时用到 yield 暂停,如果执行此时只会执行一次。

for($i = 0;$i < 10000; $i++){

#此时循环只执行一次:除非有内容触发循环再次执行(需要 $i)循环才会开始下一次

yield $i;

}

4.yield 关键字代表暂停代码继续向下执行:直到 yield 代码被使用(循环遍历)

5.生成器对象遍历:使用 yield 后,函数就会返回一个 Generator 的对象,此时就可以针对对象进行遍历。只要有了 yield 系统会自动匹配 Generator

此时需要一个函数让对象返回。需要函数对 yield 进行包装:函数才有返回值。

 

三、步骤

1、需要使用大规模数据产生(数组或者对象,一般针对数组,因为对象不会很大不会有很多数据和属性,而数组是专门用来解决大数据的问题)

2、需要对数据产生后进行遍历,因为获取就是为了输出

3、为了节省内存的使用:使用生成器


四、示例

1. 需要产生10000个数据的数组并进行遍历

传统实现方式:函数+遍历。先定义一个函数,循环一万次,任何把数据存到数组里,把数组再返回,外部拿到数组后再 foreach 取出来。

function getArr(){

for($i = 0;$i < 10000; $i++){

$arr[] = $i;

}

return $arr;

}

$arr = getArr();

foreach($arr as $v){

echo $v . ' ';

}

这种方式消耗的内存可以通过此方法获得:

#普通方式进行数组遍历

function getArr(){

for($i = 0;$i < 1000;$i++){

$arr[] = $i;

}

return $arr;

}

echo memory_get_usage() , ' '; #取出当前 PHP 所占用的内存

$arr = getArr();

foreach($arr as $v){

}

echo memory_get_usage(),'
';

结果为

图片1.png图片2.png

此时表示1000个数据用了37兆多的内存,10000个数据则是用到了几百 k,这是因为数组在内存里存着很占空间。

使用生成器:函数(生成器)+遍历: for 里不用数组来保存,直接 yield $i,到这里就会暂停,然后用 $g 获取内容,获取到了进行遍历。但是遍历不再使用原来的方式。

function getArr(){

for($i = 0;$i < 10000;$i++){

yield $i;

}

}

$g = getArr();

foreach($g as $v){

echo $v . ' ';

}

遍历之前可以打印看到内容,生成器的对象,一定要用函数来做才能得到对应生成器的对象,如果只用 yield 没办法返回。

为避免重复改 Arr 为 Ger:

function getGer({

for($i = 0;$i < 10000;$i++){

yield $i;

}

}

echo memory get_usage(), '
';

$g = getGer();

var_dump($g);

foreach($g as $v){

#echo $v . ' ';

}

echo memory get_usage(), '
';

图片3.png

此时再来看它所占用的内存 可以看到同样10000个数据只占了两百兆。

图片4.png

原因是当我们定义函数运行时函数不会运行,但是调用函数时就会运行,此时运行到 yield 会暂停,所以当拿到 $g 对象的时候函数运行停留在 yield $i; 直到把 Generator 生成器进行遍历,取 $v,这意味着要用一次 yield,给到 $i 之后输出,再进行下一次 foreach。当取出之后系统就会自动执行,再按照函数循环的方式执行,然后到 yield 暂停,等待下一次使用。言外之意在内存里面根本没有10000个数据的数组,始终只有 $i 变量从1到9999,保存一个整数变量和保存10000个元素数组的变量占用的空间完全不同,所以它的核心在于因为 yield 空间被释放。

2. 生成器实际运用

从数据库获取一张表所有记录,然后输出所有记录到表格。正常做法是连接认证,设置字符集,写一个函数 query,再全部放到数组里面。如果记录非常大,就可以使用 yield 来做一次就取一条记录然后暂停,在下一次要的时候调用 query 把数据查出来,然后进行遍历输出。一个 v 就是一个 row,就是一个数组,一个数组里面取出下标,然后取出对应元素。

$conn = @mysq1i_connect(' localhost' , ' root' , ' root ' , 'db_2' , '3306') or die ('数据库连 mysqli_set_charset($conn , 'utf8') or die ('字符集设置失败! ');

function query($conn, $sq1){

$res = mysqli_query($conn,$sq1);

while($row = mysqli_fetch_assoc($res)){

yield $row;          #这样就不需要数组保存大数据了,如果数据量够大会产生很大的消耗

}

}

#遍历输出

$list = query($conn, 'select * from t_40");

echo '';

foreach($list as $v){

echo <<

图片5.png

在里面运用了一个函数来封装了查询操作,本来是给出二位数组,然后遍历成一维再去输出,可以达到同样的效果。

然后 echo memory__get__usage(),"
' ;

可以看到内存多少,大概使用了 16k 的内存

图片6.png

结果为

图片7.png

如果用 yield 的方式来实现:

yield  $row;        #这样就不需要数组保存大数据了,如果数据量够大会产生很大的消耗

}

# return $list;

}

echo memory_get_usage(o, '
";

结果为

图片8.png

只用了不到2k的内存,实现了内存消耗的减少。也可以做时间的判定,看哪个时间少效率高,用对应的 time 相减。在后续进行大数据的使用时,从数据库取出显示的时候可以使用生成器来优化内存占用。


五、小结

1、生成器是一种实现了 lterator 迭代器接口的类,这种类不需要我们主动调用,它会自动地触发,一旦某一个函数里面用到了 yield 关键字,系统就会自动返回 Generator 的对象,对象就接管了 foreach 的内部运行原理,自己去控制。

2、生成器的目的是利用 yield 关键字实现循环内部的暂停(这种暂停让以前必须把所有东西取出来存放到很大的变量里面变成数组或二位数组的方式被 yield 打断,简化成一个简单变量),而直到 yield 被使用使用循环才会继续执行,从而节省通过循环产生一个大数组的过程(函数内部生成数组本身也是这样内存,但是过程检测不到,因为函数的执行是内部的过程,这个过程把生成大数组,函数生成和外面保存两部分对内存的消耗都解决掉),最终实现内存优化

3、在大型数据展示的时候(数据库数据操作、文件读取)都建议使用生成器来实现内存解析(面试题常问:一个 10G 的文件,但只有 2G 内存,应该怎么读。应该写一个函数,里面用生成器,保证其他程序正常运行,不占用内存)

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
2月前
|
前端开发
ES6之生成器(Generator)
生成器(Generator)是ES6引入的一种特殊的函数,它可以通过yield关键字来暂停函数的执行,并返回一个包含value和done属性的对象。生成器的概念、作用和原理如下所述:
40 0
|
2月前
|
Python
|
Python
【Python】创建生成器generator
【Python】创建生成器generator
44 0
|
机器学习/深度学习 算法 Python
python中生成器generator
python中生成器generator
|
安全 Python
一日一技:一个生成器如何当两个用?
一日一技:一个生成器如何当两个用?
70 0
ES6 从入门到精通 # 15:生成器 Generator 的用法
ES6 从入门到精通 # 15:生成器 Generator 的用法
81 0
ES6 从入门到精通 # 15:生成器 Generator 的用法
|
设计模式 缓存
TinyId生成器
TinyId生成器 的nextId、getNextSegmentId,一个是获取segmentId,一个是获取nextId。也即生成的过程中,首先会生成一批数据的maxId和delta、reminder等信息,然后获取nextId。而这个过程中,首先需要有idGenerator对象。目前可以看到其多次使用double check,基于单例模式。同时基于缓存,使用了抽象工厂模式,获取idGenerator的时候。
254 0
TinyId生成器
|
JSON 分布式计算 数据格式
Follwfile 生成器1 | 学习笔记
快速学习 Follwfile 生成器1
112 0
Follwfile 生成器1  |  学习笔记
|
开发者 Python
生成器的使用介绍 | 学习笔记
快速学习 生成器的使用介绍
92 0
|
机器学习/深度学习 开发者 Python
生成器的练习 | 学习笔记
快速学习 生成器的练习
65 0