Python从生成器到列表

时间:2016-01-10 09:03:17

标签: python list generator yield

我已经看到了一些示例,我们可以将generator转移到list,如下所示。

第一个例子:

print [2 * n for n in range(5)]
# same as the list comprehension above
print list(2 * n for n in range(5))    

第二个例子:

def double(L):
    for x in L:
        yield x*2

# eggs will be a generator
eggs = double([1, 2, 3, 4, 5])
# the above is equivalent to ("generator comprehension"?)
eggs = (x*2 for x in [1, 2, 3, 4, 5])
# need to do this if you need a list
eggs = list(double([1, 2, 3, 4, 5]))
print eggs
# the above is equivalent to (list comprehension)
eggs = [x*2 for x in [1, 2, 3, 4, 5]]
print eggs

我的问题是,所有generators都可以转移到list吗?(我在下面的例子中失败了):

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

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

generator = get_primes(5)
print list(generator)

输出:TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int'

问题在于:number = yield number由于某种原因。对此有一些解释。

3 个答案:

答案 0 :(得分:3)

你的发电机坏了:

number = yield number

这应该是yield number。将yield表达式的值分配给变量仅在您希望调用者将send值放入生成器时才有用。当您正常迭代生成器时,其所有yield表达式的计算结果为None。此行将None分配给number,然后分配number += 1 TypeErrors,因为您尝试将整数添加到None

如果您尝试使用for循环迭代此生成器,则会出现相同的错误。

也就是说,并非所有生成器都可以转换为列表,而您的代码(固定或不固定)就是原因的一个示例:生成器可能会抛出异常或永久产生值。 list构造函数大致相当于

def list(arg):
    l = []
    for item in arg:
        l.append(arg)
    return l

如果生成器抛出异常,异常将从list构造函数传播出来并终止循环。如果发生器永远产生,那么循环将永远存在,或者至少在你内存不足或耐心耗尽之前。你也可能有一台拒绝屈服的发电机:

def noyield():
    while True:
        pass
    yield 1  # Not happening.

答案 1 :(得分:1)

这里有两个问题:

number = yield number

会将number设置为None(因为您没有send生成器的任何内容。

第二个问题是:你的发电机永远不会终止。如果你从那个python生成一个列表可能会遇到内存溢出。

这是你可以做的:

import math

def get_primes(start, stop):
    n = start
    while True:
        if n >= stop:
            raise StopIteration
        if is_prime(n):
            yield n
        n += 1

def is_prime(number):
    # no changes here

generator = get_primes(5, 15)
print list(generator)  # [5, 7, 11, 13]

任何不会引发除StopIteration以外的异常并终止的生成器都可以转换为列表。

答案 2 :(得分:1)

更改

number += 1

number = number + 1 if (number is not None) else 1

通过这种方式,只有在发送内容时才会更新号码并避免上述异常。 但如果您没有向生成器发送值,则会覆盖数字。

@hiro主角描述的两个问题仍然存在!