from functools import lru_cache@lru_cache()deffib_recursive(n):if n <=1:return1else:returnfib_recursive(n-1)+fib_recursive(n-2)
let's create an iterator approach so we can iterate over the sequence, but without materializing it
deffib_gen(n): fib_0 =1yield fib_0 fib_1 =1yield fib_1for i inrange(n-1): fib_0, fib_1 = fib_1, fib_0 + fib_1yield fib_1 [num for num infib_gen(7)]Out:[1,1,2,3,5,8,13,21]
Making an Iterable from a Generator
As we now know, generators are iterators.
This means that they become exhausted - so sometimes we want to create an iterable instead.
There's no magic here, we simply have to implement a class that implements the iterable protocol:
But, sq was an iterator - so now it's been exhausted, To restart the iteration we have to create a new instance of the generator,let's wrap this in an iterable:
Card Deck
We can now make it into an iterable:
One thing we don't have here is the support for reversed,but we can add it in by implementing the __reversed__ method and returning an iterator that iterates the deck in reverse order.
Yield From
Example
Here's an example where using yield from can be quite effective.
In this example we need to read car brands from multiple files to get it as a single collection.
We might do it this way:
We can simplify our function by using yield from:
So, we are going to create generators that can read each line of the file, and yield a clean result, and we'll yield from that generator:
As you can see, this generator function will clean each line of the file before yielding it. Let's try it with a single file and make sure it works:
Ok, that works. So now, we can proceed with our overarching generator function as before, except we'll yield from our generators, instead of directly from the file iterator:
def squares_gen(n):
for i in range(n):
yield i ** 2
sq = squares_gen(5)
for num in sq:
print(num)
0
1
4
9
16
next(sq)
StopIteration
class Squares:
def __init__(self, n):
self.n = n
@staticmethod
def squares_gen(n):
for i in range(n):
yield i ** 2
def __iter__(self):
return Squares.squares_gen(self.n)
from collections import namedtuple
Card = namedtuple('Card', 'rank, suit')
SUITS = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
RANKS = tuple(range(2, 11)) + tuple('JQKA')
def card_gen():
for suit in SUITS:
for rank in RANKS:
card = Card(rank, suit)
yield card
for card in card_gen():
print(card)
Card(rank=2, suit='Spades')
Card(rank=3, suit='Spades')
Card(rank=4, suit='Spades')
Card(rank=5, suit='Spades')
Card(rank=6, suit='Spades')
Card(rank=7, suit='Spades')......
deck = CardDeck()
print(len[card for card in deck])
54
brands = []
with open('car-brands-1.txt') as f:
for brand in f:
brands.append(brand.strip('\n'))
with open('car-brands-2.txt') as f:
for brand in f:
brands.append(brand.strip('\n'))
with open('car-brands-3.txt') as f:
for brand in f:
brands.append(brand.strip('\n'))
for brand in brands:
print(brand, end=', ')
def brands(*files):
for f_name in files:
with open(f_name) as f:
yield from f
for brand in brands(*files):
print(brand, end=', ')
Alfa Romeo
, Aston Martin
, Audi
, Bentley
, Benz
, BMW
, Bugatti
, Cadillac
, Chevrolet
, Chrysler
, Citroën
, Corvette
, DAF
, Dacia
, Daewoo
, Daihatsu
......
def gen_clean_read(file):
with open(file) as f:
for line in f:
yield line.strip('\n')
f1 = gen_clean_read('car-brands-1.txt')
for line in f1:
print(line, end=', ')
Alfa Romeo, Aston Martin, Audi, Bentley, Benz, BMW,Bugatti...
files = 'car-brands-1.txt', 'car-brands-2.txt', 'car-brands-3.txt'
def brands(*files):
for file in files:
yield from gen_clean_read(file)
for brand in brands(*files):
print(brand, end=', ')
Alfa Romeo, Aston Martin, Audi, Bentley, Benz, BMW,Bugatti...
# 注:为了加强示例代码的说明性,本文中的部分代码片段使用了Python 3.5
# 版本添加的 Type Hinting 特性
def add_ellipsis(comments: typing.List[str], max_length: int = 12):
"""如果评论列表里的内容超过 max_length,剩下的字符用省略号代替
"""
index = 0
for comment in comments:
comment = comment.strip()
if len(comment) > max_length:
comments[index] = comment[:max_length] + '...'
index += 1
return comments
comments = [
"Implementation note",
"Changed",
"ABC for generator",
]
print("\n".join(add_ellipsis(comments)))
Implementati...
Changed
ABC for gene...
============================================================
def add_ellipsis_gen(comments: typing.Iterable[str], max_length: int = 12):
"""如果可迭代评论里的内容超过 max_length,剩下的字符用省略号代替
"""
for comment in comments:
comment = comment.strip()
if len(comment) > max_length:
yield comment[:max_length] + '...'
else:
yield comment
print("\n".join(add_ellipsis_gen(comments)))
Implementati...
Changed
ABC for gene...