Python中Generators教程

什么是generator?

generator是一种特殊的函数,和一般的函数不同,一般的函数调用一次,总会结束返回;generator却可以执行到某个位置停住,通过yield让出执行权,下次再调用时,从上一次yield后面的地方开始执行。

例如:

def generator():
 yield "a"
 yield "b"
 yield "c"
for i in generator():
 print(i);

怎样创建一个python generator?

就像创建一个函数一样简单,只不过不使用return 声明,而是使用yield声明.

如果一个函数至少包含一个yield声明(当然它也可以包含其他yield或return),那么它就是一个generator. 

yield和return都会让函数返回一些东西,区别在于,return声明彻底结束一个函数,而yield声明是暂停函数,保存它的所有状态,并且后续被调用后会继续执行.

generator函数和普通函数的区别

  • generator函数包含一个以上的yield声明
  • generator函数被调用的时候,会返回一个iterator对象,但是函数并不会立即开始执行
  • __iter__()和__next__()方法被自动实现,所以可以使用next()函数对返回的此iterator对象进行迭代
  • 一旦一个generator 执行到yield语句,generator函数暂停,程序控制流被转移到调用方
  • 在对generator的连续调用之间,generator的本地变量和状态会被保存
  • 最终,generator函数终止,再调用generator会引发StopIteration异常

下面这个例子说明上述全部要点,我们有一个名为my_gen()的函数,它带有一些yield声明.

# A simple generator function  
def my_gen():  
    n = 1  
    print('This is printed first')  
    # Generator function contains yield statements  
    yield n  

    n += 1  
    print('This is printed second')  
    yield n  

    n += 1  
    print('This is printed at last')  
    yield n  

if __name__ == '__main__':
    a = my_gen()
    print a.next()
    print a.next()
    print a.next()
返回结果:

This is printed first
1
This is printed second
2
This is printed at last
3

有趣的是,在这个例子里变量n在每次调用之间都被记住了。和一般函数不同的是,在函数yield之后本地变量没有被销毁,而且,generator对象只能被这样迭代一次。要想重复上面的过程,需要类似 a = my_gen() 这样创建另一个generator对象,并对其使用next方法迭代。

我们还可以对generator对象直接使用for循环,这是因为一个for循环接收一个iterator对象,且使用next()函数迭代它,当遇到StopIteration异常的时候自动停止。

# A simple generator function  
def my_gen():  
    n = 1  
    print('This is printed first')  
    # Generator function contains yield statements  
    yield n  

    n += 1  
    print('This is printed second')  
    yield n  

    n += 1  
    print('This is printed at last')  
    yield n  

for item in my_gen():  
    print(item) 

有循环的python generator

reverse一个字符串例子:

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1, -1, -1): # 第三个参数为步长,负数表示反向,第三个参数,表示到0结束
        yield my_str[i]

for char in rev_str("hello")
    print char
返回结果:
Ouput:
o
l
l
e
h

python里为什么要使用generator?

1.容易实现

相对于iterator类来说,generator的实现清晰、简洁。下面是用iterator实现一个2的指数函数

class PowTwo:
    def __init__(self,max = 0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self
    
    def __next__(self):
        if self.n > self.max:
            raise StopIteration # 抛出问题,停止迭代

        result = 2 ** self.n

generator这样实现

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2**n
        n += 1

因为generator自动跟踪实现细节,因此更加清晰、简洁。

2.节省内存

一个函数返回一个序列(sequence)的时候,会在内存里面把这个序列构建好再返回。如果这个序列包含很多数据的话,就过犹不及了。

而如果序列是以generator方式实现的,就是内存友好的,因为他每次只产生一个item。

3.代表无限的stream

generator是一个很棒的表示无限数据流的工具。无限数据流不能被保存在内存里面,并且因为generator每次产生一个item,它就可以表示无限数据流。

下面的代码可以产生所有的奇数

def all_even():
    n = 1
    while True:
        yield n
        n += 2

4.generator流水线(pipeline)

generator可以对一系列操作执行流水线操作。

假设我们有一个快餐连锁店的日志。日志的第四列是每小时售出的披萨数量,我们想对近5年的这一数据进行求和。

假设所有数据都是字符,不可用的数据都以"N/A"表示,使用generator可以这样实现

with open('sells.log') as file:
    pizza_col = (line[3] for line in file) # 遍历file,然后返回line[3]
    per_hour = (int(x) for x in pizza_col if x != 'N/A')
    print "Total pizzas sold = ",sum(per_hour) # per_hour时可迭代对象

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值