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:

python
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:

text
Backend: Budi
Backend: Siti
Data: Rina
Data: Ari

Mari kita bedah perannya:

  1. yield from tim("Backend", [...]): generator semua_peserta() berhenti sejenak dan membiarkan generator tim() menyalurkan semua nilainya langsung ke pemanggil. Setelah tim() habis, kendali kembali ke baris berikutnya.
  2. 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:

python
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:

text
generator manual mulai
hasil manual    : [1, 2]
generator yield from mulai
hasil yield from: [1, 2]
AspekLoop manual for ... yieldyield from
Baris kodeDua baris per sumberSatu baris per sumber
Menangkap return sub-generatorTidak bisaBisa, sebagai hasil ekspresi
send() dan throw() diteruskanTidak otomatisDiteruskan 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:

python
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:

text
[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:

python
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:

text
menyiapkan data
memvalidasi format
sub-proses mengembalikan: laporan-final.csv
berkas siap: laporan-final.csv

Di 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:

python
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:

text
1
StopIteration.value: nilai_rahasia

Tanpa 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.