Генератори ліниво оцінюють так return
чиyield
будуть поводитись інакше, коли ви налагоджуєте код або якщо буде викинуто виняток.
За return
будь-яким винятком, що трапляється у вас, generator
ви нічого не будете знати про generate_all
це, тому що, коли generator
це справді виконується, ви вже покинули generate_all
функцію. З yield
там, він буде мати generate_all
у сліді.
def generator(some_list):
for i in some_list:
raise Exception('exception happened :-)')
yield i
def generate_all():
some_list = [1,2,3]
return generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
8 return generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-3-b19085eab3e1> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
І якщо він використовує yield from
:
def generate_all():
some_list = [1,2,3]
yield from generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
8 yield from generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-4-be322887df35> in generate_all()
6 def generate_all():
7 some_list = [1,2,3]
----> 8 yield from generator(some_list)
9
10 for item in generate_all():
<ipython-input-4-be322887df35> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
Однак це відбувається ціною продуктивності. Додатковий шар генератора має певні накладні витрати. Так що return
, як правило, трохи швидше, ніж yield from ...
(абоfor item in ...: yield item
). У більшості випадків це не має великого значення, оскільки те, що ви робите в генераторі, як правило, домінує під час роботи, так що додатковий шар не буде помітний.
Однак yield
є деякі додаткові переваги: ви не обмежені одним ітерабельним, ви також можете легко отримати додаткові елементи:
def generator(some_list):
for i in some_list:
yield i
def generate_all():
some_list = [1,2,3]
yield 'start'
yield from generator(some_list)
yield 'end'
for item in generate_all():
print(item)
start
1
2
3
end
У вашому випадку операції досить прості, і я не знаю, чи потрібно навіть для цього створити кілька функцій, можна просто використовувати вбудований map
або генераторний вираз замість цього:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Обидва мають бути однаковими (за винятком деяких відмінностей, коли трапляються винятки) для використання. І якщо їм потрібна більш описова назва, то ви все одно можете їх зафіксувати в одній функції.
Існує декілька помічників, які обробляють дуже поширені операції над вбудованими ітерабелізаторами, а подальші можна знайти у вбудованому itertools
модулі. У таких простих випадках я б просто вдався до цих і лише для нетривіальних випадків напишіть власні генератори.
Але я припускаю, що ваш справжній код є складнішим, тому він може не застосовуватися, але я подумав, що це не буде повною відповіддю без згадування альтернатив.