Generatoren werden faul ausgewertet return
oder yield
verhalten sich anders, wenn Sie Ihren Code debuggen oder wenn eine Ausnahme ausgelöst wird.
Mit return
Ausnahme der Fälle, in denen Sie generator
nichts wissen, liegt dies daran generate_all
, dass generator
Sie die generate_all
Funktion bereits verlassen haben, wenn sie wirklich ausgeführt wird . Mit yield
drin wird es generate_all
im Traceback sein.
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 :-)
Und wenn es verwendet 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 :-)
Dies geht jedoch zu Lasten der Leistung. Die zusätzliche Generatorschicht hat einen gewissen Overhead. Also return
wird in der Regel etwas schneller als yield from ...
(oder for item in ...: yield item
). In den meisten Fällen spielt dies keine große Rolle, da alles, was Sie im Generator tun, normalerweise die Laufzeit dominiert, sodass die zusätzliche Ebene nicht wahrgenommen wird.
Hat yield
jedoch einige zusätzliche Vorteile: Sie sind nicht auf eine einzige iterable beschränkt, sondern können auch problemlos zusätzliche Elemente liefern:
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
In Ihrem Fall sind die Operationen recht einfach und ich weiß nicht, ob es überhaupt notwendig ist, mehrere Funktionen dafür zu erstellen. Man könnte map
stattdessen einfach den eingebauten oder einen Generatorausdruck verwenden:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Beide sollten identisch sein (mit Ausnahme einiger Unterschiede, wenn Ausnahmen auftreten). Und wenn sie einen aussagekräftigeren Namen benötigen, können Sie sie dennoch in eine Funktion einschließen.
Es gibt mehrere Helfer, die sehr häufige Operationen für integrierte Iterables umschließen, und weitere finden Sie im integrierten itertools
Modul. In solch einfachen Fällen würde ich einfach auf diese zurückgreifen und nur für nicht triviale Fälle Ihre eigenen Generatoren schreiben.
Aber ich gehe davon aus, dass Ihr echter Code komplizierter ist, so dass er möglicherweise nicht anwendbar ist, aber ich dachte, es wäre keine vollständige Antwort, ohne Alternativen zu erwähnen.