У меня есть генератор, generator
а также удобный метод generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
Должен generate_all
return
или yield
? Я хочу, чтобы пользователи обоих методов использовали его одинаково, т.е.
for x in generate_all()
должно быть равно
some_list = get_the_list()
for x in generate(some_list)
Ответы:
Генераторы лениво оценивают так
return
илиyield
будут вести себя по-другому, когда вы отлаживаете код или выдается исключение.За
return
исключением любых случаев, когда выgenerator
ничего не знаетеgenerate_all
, это происходит потому, что когдаgenerator
вы действительно выполняете, вы уже вышли изgenerate_all
функции. Сyield
там это будетgenerate_all
в трассировке.И если он использует
yield from
:Однако это происходит за счет производительности. Дополнительный уровень генератора имеет некоторые накладные расходы. Так
return
что, как правило, будет немного быстрее, чемyield from ...
(илиfor item in ...: yield item
). В большинстве случаев это не имеет большого значения, потому что все, что вы делаете в генераторе, обычно доминирует во время выполнения, так что дополнительный слой не будет заметен.Тем
yield
не менее, имеет ряд дополнительных преимуществ: вы не ограничены одной итерацией, вы также можете легко получить дополнительные элементы:В вашем случае операции довольно просты, и я не знаю, нужно ли вообще создавать несколько функций для этого,
map
вместо этого можно было бы просто использовать встроенное выражение или выражение генератора:Оба должны быть идентичны (за исключением некоторых различий, когда случаются исключения) для использования. И если им нужно более описательное имя, вы все равно можете заключить их в одну функцию.
Есть несколько помощников, которые обертывают очень распространенные операции для встроенных итераторов, а дополнительные можно найти во встроенном
itertools
модуле. В таких простых случаях я бы просто прибегнул к этим и только для нетривиальных случаев написал свои собственные генераторы.Но я предполагаю, что ваш реальный код более сложный, поэтому он может быть неприменим, но я подумал, что это не будет полным ответом без упоминания альтернатив.
источник
Вы, вероятно, ищете делегирование генератора (PEP380)
Это довольно лаконично, а также имеет ряд других преимуществ, таких как возможность связывать произвольные / разные итерации!
источник
list
? Это плохой пример, а не настоящий код, вставленный в вопрос, я, вероятно, должен его отредактировать.yield from map(do_something, iterable)
или дажеyield from (do_something(x) for x in iterable)
yield from
это бессмысленно, если ваша обертка не делает что - то еще generator-y.return generator(list)
делает то, что вы хотите. Но учтите, чтобудет эквивалентно, но с возможностью получить больше значений после того,
generator
как исчерпан. Например:источник
yield from
и ,return
когда потребитель генератораthrows
исключение внутри него - и с другими операциями , которые находятся под влиянием трассировки стека.Следующие два утверждения окажутся функционально эквивалентными в данном конкретном случае:
а также
Последний примерно такой же, как
return
Оператор возвращает генератор , который вы ищете.yield from
Илиyield
оператор поворачивает всю функцию в то , что возвращает генератор, который проходит через один вы ищете.С точки зрения пользователя, нет никакой разницы. Внутренне, однако,
return
это, возможно, более эффективно, так как оно не оборачиваетсяgenerator(list)
в избыточном сквозном генераторе. Если вы планируете выполнять какую-либо обработку элементов обернутого генератора,yield
конечно , используйте некоторую форму .источник
Вы бы
return
это.yield
ing * приведетgenerate_all()
к вычислению самого генератора и вызовуnext
этого внешнего генератора вернет внутренний генератор, возвращенный первой функцией, а это не то, что вам нужно.*
Не включаяyield from
источник