你對生成器瞭解多少?
最近在學習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關鍵字來實現的,而且一般都是用在迴圈中。在迴圈大陣列的時候會很大的提升效能。