2016-01-10 4 views
1

Я видел несколько примеров, где мы можем передать generator в list, как показано ниже.Python от генератора к списку

Первый пример:

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 почему-то. Некоторое объяснение этому оценивается.

+0

Что? 'number = yield number'? 'yield' ничего не возвращает. –

+0

Я отредактирую его, я использую его для проверки send() раньше. –

+0

Не редактируйте его, потому что эта строка - это то, почему вы получаете этот TypeError. – user2357112

ответ

3

Ваш генератор сломана:

number = yield number 

Это должно быть просто yield number. Присвоение значения выражения yield переменной применимо только тогда, когда вы ожидаете, что вызывающий объект будет иметь значения send в генераторе. Когда вы просто перебираете генератор нормально, все его выражения доходности оцениваются до None. Эта строка присваивает Nonenumber, а затем 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. 
+0

Это такой быстрый и идеальный ответ. –

1

есть 2 вопроса здесь:

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 и завершаясь могут быть преобразованы в список.

1

Изменить

number += 1 

в

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

В этом случае вы бы только обновить номер, если что-то послал к нему и избежать исключения, упомянутых выше. Но вы бы переопределили число, если вы не отправите значение генератору.

Но два вопроса, описанные @hiro главный герой все еще существуют!

Смежные вопросы