Ketika satu generator perlu menyalurkan nilai dari beberapa sumber sekaligus, menulis loop for lalu yield untuk setiap sumber membuat kode berlapis-lapis. Kata kunci yield from (hadir sejak Python 3.3) menyederhanakan komposisi tersebut: satu baris yield from mendelegasikan seluruh pekerjaan menarik nilai ke generator atau iterable lain, ditambah satu bonus penting — nilai return dari sub-generator bisa ditangkap sebagai hasil.
Delegasi ke Generator Lain #
Bayangkan dua tim yang pesertanya harus digabung dalam satu daftar peserta. Tanpa yield from, kita menulis loop di dalam loop:
def tim(nama, anggota):
for orang in anggota:
yield f"{nama}: {orang}"
def semua_peserta():
yield from tim("Backend", ["Budi", "Siti"])
yield from tim("Data", ["Rina", "Ari"])
for peserta in semua_peserta():
print(peserta)Output yang Dihasilkan:
Backend: Budi
Backend: Siti
Data: Rina
Data: AriMari kita bedah perannya:
yield from tim("Backend", [...]): generatorsemua_peserta()berhenti sejenak dan membiarkan generatortim()menyalurkan semua nilainya langsung ke pemanggil. Setelahtim()habis, kendali kembali ke baris berikutnya.- Pemanggil (
for peserta in ...): tidak melihat adanya dua generator; pemanggil hanya menerima satu aliran datar berisi empat baris.
Manual versus yield from #
Untuk memperjelas apa yang disederhanakan, bandingkan dua bentuk yang setara:
def angka_manual():
print("generator manual mulai")
for n in [1, 2]:
yield n
def angka_from():
print("generator yield from mulai")
yield from [1, 2]
print("hasil manual :", list(angka_manual()))
print("hasil yield from:", list(angka_from()))Output yang Dihasilkan:
generator manual mulai
hasil manual : [1, 2]
generator yield from mulai
hasil yield from: [1, 2]| Aspek | Loop manual for ... yield | yield from |
|---|---|---|
| Baris kode | Dua baris per sumber | Satu baris per sumber |
Menangkap return sub-generator | Tidak bisa | Bisa, sebagai hasil ekspresi |
send() dan throw() diteruskan | Tidak otomatis | Diteruskan otomatis ke sub-generator |
Ketiga baris pada tabel, terutama baris kedua, adalah alasan yield from bukan sekadar gula sintaksis.
Meratakan List Bersarang #
yield from berpasangan mulus dengan recursion karena setiap elemen bersarang cukup didelegasikan ke pemanggilan rekursif — teknik recursion-nya sendiri sudah kita bahas pada Chapter Recursion:
def ratakan(bersarang):
for item in bersarang:
if isinstance(item, list):
yield from ratakan(item)
else:
yield item
data = [1, [2, 3], [4, [5, 6]], 7]
print(list(ratakan(data)))Output yang Dihasilkan:
[1, 2, 3, 4, 5, 6, 7]Ketika item ternyata list, yield from ratakan(item) membuka lapisan itu lebih dalam lagi hingga menemui nilai biasa; nilai biasa diserahkan langsung lewat yield item. Struktur bersarang berapa pun tingkatnya rata menjadi satu aliran.
Menangkap Nilai return Sub-Generator #
Bagian yang paling khas dari yield from: sub-generator boleh mengembalikan nilai dengan return, dan generator pemanggil menangkapnya sebagai hasil ekspresi yield from:
def sub_proses():
yield "menyiapkan data"
yield "memvalidasi format"
return "laporan-final.csv"
def proses_utama():
nama_hasil = yield from sub_proses()
print(f"sub-proses mengembalikan: {nama_hasil}")
yield f"berkas siap: {nama_hasil}"
for pesan in proses_utama():
print(pesan)Output yang Dihasilkan:
menyiapkan data
memvalidasi format
sub-proses mengembalikan: laporan-final.csv
berkas siap: laporan-final.csvDi balik layar, return "laporan-final.csv" membuat interpreter Python membungkus nilai tersebut ke dalam StopIteration sebelum diteruskan ke generator pemanggil. Kita bisa melihat bungkus itu secara langsung:
def sub():
yield 1
return "nilai_rahasia"
gen = sub()
print(next(gen))
try:
next(gen)
except StopIteration as galat:
print("StopIteration.value:", galat.value)Output yang Dihasilkan:
1
StopIteration.value: nilai_rahasiaTanpa yield from, generator pemanggil hanya melihat StopIteration sebagai tanda habis dan nilai nilai_rahasia hilang. Dengan yield from, penangkapan StopIteration.value tersebut diotomatiskan menjadi satu baris nama_hasil = yield from sub_proses().
Dari sini kita tinggal selangkah menuju puncak materi ini: merangkai generator menjadi pipeline yang menilai data secara lazy dari hulu ke hilir.