Generator 生成器|学习笔记

简介: 快速学习 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的热门话题分析
Apsara Clouder大数据专项技能认证配套课程:基于MaxCompute的热门话题分析
相关文章
|
算法 5G 数据处理
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
342 0
logback为日志配置颜色
logback为日志配置颜色
344 1
|
7月前
|
IDE 文件存储 开发工具
鸿蒙开发:应用上架第一篇,生成密钥和证书请求文件
本系列文章,我们就着重概述一下,在鸿蒙当中,如何打出一个上架包,一个上架包的产出,需要多个步骤,本篇文章,我们先从第一步骤进行讲解,也就是如何生成秘钥和证书请求文件。
170 13
鸿蒙开发:应用上架第一篇,生成密钥和证书请求文件
|
6月前
|
存储 算法 数据可视化
用Python开发猜数字游戏:从零开始的手把手教程
猜数字游戏是编程入门经典项目,涵盖变量、循环、条件判断等核心概念。玩家通过输入猜测电脑生成的随机数,程序给出提示直至猜中。项目从基础实现到功能扩展,逐步提升难度,适合各阶段Python学习者。
494 0
|
存储 文件存储 数据库
对象存储、块存储、文件存储他们都有什么不通的作用?
对象存储、块存储、文件存储他们都有什么不通的作用?
2552 2
|
11月前
|
运维 监控 安全
HTTPS 证书自动化运维:使用Certbot来申请https证书实践指南
本文深入探讨HTTPS证书自动化运维,提供实践指南与案例分析。首先介绍选择合适的工具和平台,如Certbot、ACME客户端及图形化管理系统的应用。接着详细讲解使用Certbot签发Let’s Encrypt证书的步骤,并强调安全策略、权限管理和监控日志的重要性。通过中小型企业与大型电商平台的实际案例,展示自动化运维的优势。最后针对常见问题提供解决方案,帮助读者实现高效、安全的证书管理。
|
数据库 数据库管理
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
1454 0
|
SQL 算法 Java
MyBatis-Plus详解(3)
MyBatis-Plus详解(3)
255 0
|
计算机视觉
YOLOv5改进 | Conv篇 | 轻量级下采样方法ContextGuided(大幅度涨点)
YOLOv5改进 | Conv篇 | 轻量级下采样方法ContextGuided(大幅度涨点)
543 0
|
API PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(九)(3)
PyTorch 2.2 中文官方教程(九)
583 0
PyTorch 2.2 中文官方教程(九)(3)