Python的yield 问题

Python yield示范

yield 关键词在python中不算常用。以前我只知道它的作用和return相似,就是返回一个值。并不知道它具体的用途和用法。

要理解yield,你必须知道generators(生成器)。而要知道生成器,需要了解iterables(可迭代对象)

生成器(Generators)就是迭代器的一种特殊形式,只不过它只能迭代一次。这是因为生成器不是将所有的数值存储在内存里,而是,立即生成数值。
和迭代器相似,我们可以通过使用next()来从generator中获取下一个值

顺便说说:集合数据类型,如list、tuple、dict、set、str等,这些可以用for循环的对象都是可迭代对象(Iterable)。
但list、dict、str虽然是Iterable,却不是迭代器(Iterator)。把list、dict、str等Iterable变成Iterator可以使用iter()函数。

迭代器对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。

Python的for循环本质上就是通过不断调用next()函数实现的

我们看看下面例子:

测试环境是ubuntu;
Python版本为2.7

def yield_test1():
    print 'one'
    yield 1
    print 'two'
    yield 2
    print 'three'
    yield 3
    print 'is any thing?'

>y=yield_test()
>y.next()   #第一次调用next 返回的是第一个yield,返回的是1
>one
>[out]1
>y.next()   #第二次调用next 返回的是第二个yield,返回的是2
>two
>[out]2
>y.next()   #第三次调用next 返回的是第三个yield,返回的是3
>three
>[out]3
>y.next()   #第四次调用next,就不一样了,会报错StopIteration。因为已经没有东西可以yield了。但是注意,还是可以print 'is any thing'的。
>is any thing?
>StopIteration                             Traceback (most recent call last)

我的理解:yield和return的却别在于,yield是有记忆的返回东西。因为它记忆了上一次yield的值。

当一个生成器函数调用yield,生成器函数的“状态”会被冻结,所有的变量的值会被保留下来,下一行要执行的代码的位置也会被记录,直到再次调用next()。一旦next()再次被调用,生成器函数会从它上次离开的地方开始。如果永远不调用next(),yield保存的状态就被无视了。

接下来,我们运用yield在做个实例:

求出所有小于2000000的质数的和1+2+3+5+7+11+13+….
通常,我们的思路是这样for i in [所有小于2000000的质数]
sum+=i

#首先,我们定义 判断质数的方法
def is_prime(number):
if number > 1:
    if number == 2:
        return True
    if number % 2 == 0:
        return False
    for current in range(3, int(math.sqrt(number) + 1), 2):
        if number % current == 0:
            return False
    return True
return False

all_prime=[]#用一个list来装载所有的质数
for i in range(1,2000000):
    if is_prime(i):
        all_prime.append(i)

len(all_prime) #len看了下大概有66W个质数
sum=0
for i in all_prime:
    sum+=i

这种方法也可以。只不过非常消耗资源。因为要把这么大一堆质数装载到一个容器里,这占了很多空间。

我们换成另一种方法:

def sum_prime(maxnum):
    total = 0
    for next_prime in get_primes(2):#质数从2开始
        if next_prime < int(maxnum):
            total += next_prime
        else:
            print(total)
            return

def get_primes(number):
    while True:
        if is_prime(number):
            yield number
        number += 1 

sum_prime(2000000)

这样就节省了很多资源,中间也不用管它到底是有哪些质数,我只要得出总的和,结果 就够了。