Kadang satu function menjual dua perilaku sekaligus: kirim int, dapat int; kirim str, dapat str. Union menyelesaikan sisi masukan, tetapi sisi keluaran jadi kabur — nilai balik int | str memaksa pemanggil mengecek dua-duanya, bahkan ketika hubungan masukan-keluaran sebenarnya pasti. Dekorator @overload memungkinkan kita menuliskan beberapa signature sekaligus untuk satu function, sehingga checker memahami setiap kombinasi secara presisi.
Masalah: Union Masuk, Union Keluar #
Perhatikan function kali berikut:
def kali(a: int | str, b: int) -> int | str:
if isinstance(a, str):
return a * b
return a * b
print(kali(3, 4))
print(kali("kopi-", 2))Output yang Dihasilkan:
12
kopi-kopi-Signature tersebut jujur, tetapi terlalu longgar. Pemanggil yang mengirim str tahu hasilnya pasti str — a * b dengan a string pasti menghasilkan string — namun checker hanya melihat int | str dan ikut bersikap defensif. Simpan sebagai tanpa_overload.py:
def kali(a: int | str, b: int) -> int | str:
if isinstance(a, str):
return a * b
return a * b
hasil = kali("kopi-", 2)
print(hasil.upper())python3 -m mypy tanpa_overload.pyOutput yang Dihasilkan:
tanpa_overload.py:7: error: Item "int" of "int | str" has no attribute "upper" [union-attr]
Found 1 error in 1 file (checked 1 source file)mypy menolak .upper() karena menurut signature, hasil bisa saja int. Padahal setiap pembaca manusia tahu baris tersebut aman: masukan str selalu menghasilkan str. Signature union kehilangan informasi hubungan antar-parameter seperti yang kita pertahankan lewat generics pada Chapter Generic Types.
Menulis @overload #
Solusinya: daftarkan setiap kombinasi sebagai signature @overload, lalu tutup dengan satu implementasi yang menyatukan semuanya:
from typing import overload
@overload
def kali(a: int, b: int) -> int: ...
@overload
def kali(a: str, b: int) -> str: ...
def kali(a: int | str, b: int) -> int | str:
if isinstance(a, str):
return a * b
return a * b
print(kali(3, 4))
print(kali("kopi-", 2))Output yang Dihasilkan:
12
kopi-kopi-Mari kita telaah strukturnya:
- Baris 3-4 (
@overloadpertama): deklarasi “jikaabertipeintdanbbertipeint, nilai balikint”. Badannya hanya...— ini spesifikasi, bukan implementasi. - Baris 5-6 (
@overloadkedua): deklarasi “jikaabertipestr, nilai balikstr”. Checker mencocokkan pemanggilan ke deklarasi yang paling pas, berurutan dari atas. - Baris 8 (implementasi): satu function tanpa dekorator yang menampung semua kasus — signature-nya harus menerima setiap kombinasi dari deklarasi di atasnya. Hanya function inilah yang benar-benar berjalan di runtime; seluruh baris
@overloaddi atasnya diabaikan interpreter.
Sekarang checker memiliki peta yang presisi. Uji kembali kasus yang sebelumnya ditolak — simpan sebagai dengan_overload.py:
from typing import overload
@overload
def kali(a: int, b: int) -> int: ...
@overload
def kali(a: str, b: int) -> str: ...
def kali(a: int | str, b: int) -> int | str:
if isinstance(a, str):
return a * b
return a * b
hasil = kali("kopi-", 2)
print(hasil.upper())python3 -m mypy dengan_overload.pyOutput yang Dihasilkan:
Success: no issues found in 1 source fileKode .upper() yang sama, kini diterima: checker mencocokkan kali("kopi-", 2) dengan deklarasi kedua, menyimpulkan hasil pasti str, dan meloloskan pemanggilan .upper() tanpa penyempitan manual.
Kapan Memakai overloads #
Pola umum yang layak diberi @overload:
- Union masuk menentukan tipe keluar — contoh
kalidi atas. - Parameter default
None— misalnyacari(x: str) -> strdancari() -> None, dua perilaku berbeda dari satu nama. - Builder berantai — method yang mengembalikan
Selfatau tipe spesifik sesuai flag.
Jika satu function hanya punya satu perilaku, jangan buat overload — satu signature biasa sudah cukup. Overload adalah alat untuk kejujuran tipe, bukan gaya penulisan.
Konsep Stub File #
Pattern “deklarasi tanpa badan” pada @overload punya saudara besar di tingkat berkas: stub file berekstensi .pyi. Sebuah stub berisi signature dan anotasi untuk seluruh module — tanpa satu baris logika pun. Checker memakai stub tanpa membuka file .py aslinya.
Kita sudah mengalami manfaatnya secara diam-diam: mypy memeriksa len(), list.append(), dan seluruh standard library bukan dengan membaca kode C implementasinya, melainkan lewat Typeshed — kumpulan stub resmi yang dibundel mypy. Ketika sebuah library pihak ketiga tidak menyertakan anotasi, komunitas menyediakan package *-stubs dengan mekanisme yang sama. Konsepnya sama dengan @overload: pisahkan kontrak tipe dari implementasi.