最近在学习Python的时候了解到生成器(generator)的概念,觉得挺有趣的,顺便又学习了下PHP的生成器,这里就记录一下吧~
生成器,其实可以理解为是数组,但是既然它叫生成器,那肯定是和数组是有区别的。具体区别是啥呢?
我们知道,在我们声明一个数组的时候,程序会开辟一个内存来保存这个数组的数据,如果这个数组特别大,比如rang(1,1000000),这将会占用100M的内存空间,显然是不合适的。而且有时候我们可能只需要数组的前几个数据,并不一定要获取到所有的数据,这时候,生成器就派上用场了。
生成器可以实现我们在循环数据的时候,循环到哪个位置的数据再去取这个数据。之所以可以实现,是因为生成器里是保存了,数组数据的推算算法,可以理解为是一个迭代器,通过这个迭代器实现在循环的过程中,不断的推算出后面的数据,从而输出给我们。在数据显示上和循环一个数组是一样的效果~
下面是PHP手册上对生成器的解释:
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
我们分别用Python和PHP来实现下生成器
Python
我们先输出一个list
L = [x * x for x in range(10)]
print(L)
#输出
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
下面我们来实现一个生成器
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
g = (x * x for x in range(10))
print(g)
#如果我们直接打印g 将会输出如下的对象,里面存的其实是generator的迭代对象
<generator object <genexpr> at 0x0000022392C09EB0>
#如果想输出值,得用next(g)
print(next(g))
print(next(g))
0
1
但是,用next()获取值还是很鸡肋,所以还有更好的办法来获取值。我们可以用一个for循环来获取值
for n in g:
print(n)
#将会循环输出值
0
1
4
9
16
25
36
49
64
81
下面我们看一下如何用函数来实现一个生成器,其实生成器函数和普通函数基本是一样是,只是普通函数是通过return来返回值,而生成器是通过yield关键字来返回值。我们以实现斐波那契数列的函数来说明:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
for n in fib(6):
print(n)
#输出
1
1
2
3
5
8
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
f = fib(6)
while True:
try:
k = next(f)
print('k:',k)
except StopIteration as e:
print('generator return value:',e.value)
break
#输出
k: 1
k: 1
k: 2
k: 3
k: 5
k: 8
generator return value: done
以上就是用Python实现生成器
PHP
PHP的生成器和Python的实现基本是一样的,也是通过yield关键字来实现。
我们来将PHP里的range()函数用生成器的方式实现:
<?php
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* 注意下面range()和xrange()输出的结果是一样的。
*/
echo 'Single digit odd numbers from range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
以上代码会有如下输出:
Single digit odd numbers from range(): 1 3 5 7 9
Single digit odd numbers from xrange(): 1 3 5 7 9
总结:
Python和PHP中的生成器都是通过yield关键字来实现的,而且一般都是用在循环中。在循环大数组的时候会很大的提升性能。