Generator malas mengevaluasi sehingga return
atau yield
akan berperilaku berbeda ketika Anda men-debug kode Anda atau jika ada pengecualian.
Dengan return
pengecualian yang terjadi di Anda generator
tidak akan tahu apa-apa generate_all
, itu karena ketika generator
benar-benar dieksekusi Anda telah meninggalkan generate_all
fungsi. Dengan yield
di sana akan ada generate_all
di traceback.
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 :-)
Dan jika menggunakan 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 :-)
Namun ini datang pada biaya kinerja. Lapisan generator tambahan memang memiliki beberapa overhead. Jadi return
umumnya akan sedikit lebih cepat daripada yield from ...
(atau for item in ...: yield item
). Dalam kebanyakan kasus ini tidak akan terlalu menjadi masalah, karena apa pun yang Anda lakukan di generator biasanya mendominasi run-time sehingga lapisan tambahan tidak akan terlihat.
Namun yield
memiliki beberapa keuntungan tambahan: Anda tidak terbatas pada satu iterable saja, Anda juga dapat dengan mudah menghasilkan item tambahan:
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
Dalam kasus Anda, operasinya cukup sederhana dan saya tidak tahu apakah perlu membuat beberapa fungsi untuk ini, orang dapat dengan mudah menggunakan built-in map
atau ekspresi generator sebagai gantinya:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Keduanya harus identik (kecuali untuk beberapa perbedaan ketika pengecualian terjadi) untuk digunakan. Dan jika mereka membutuhkan nama yang lebih deskriptif, maka Anda masih bisa membungkusnya dalam satu fungsi.
Ada beberapa pembantu yang membungkus operasi yang sangat umum pada iterables bawaan dan yang lebih lanjut dapat ditemukan dalam itertools
modul bawaan. Dalam kasus sederhana seperti itu saya hanya akan menggunakan ini dan hanya untuk kasus non-sepele tulis generator Anda sendiri.
Tapi saya menganggap kode Anda yang sebenarnya lebih rumit sehingga mungkin tidak berlaku tetapi saya pikir itu tidak akan menjadi jawaban yang lengkap tanpa menyebutkan alternatif.