Di Python, function adalah nilai biasa: function bisa disimpan ke variabel, dikirim sebagai argument, dan dikembalikan oleh function lain — kita telah memanfaatkan hal ini saat belajar higher-order function pada Section Fungsi. Konsekuensinya, kita butuh cara menganotasi “sesuatu yang bisa dipanggil” beserta bentuk parameternya. Sementara itu, anotasi yang berguna sering tumbuh panjang dan berulang — dict[str, list[tuple[int, str]]] tiga kali dalam satu file mengundang salah ketik. Chapter ini menyelesaikan dua masalah tersebut dengan Callable dan type aliases.

Mengetik Function sebagai Nilai #

Tipe Callable hidup di module collections.abc (bentuk typing.Callable juga tersedia). Anotasinya berbentuk daftar parameter di dalam kurung siku, diikuti tipe nilai balik:

python
from collections.abc import Callable

Operasi = Callable[[int, int], int]

def hitung(a: int, b: int, operasi: Operasi) -> int:
    return operasi(a, b)

print(hitung(10, 3, lambda a, b: a + b))
print(hitung(10, 3, lambda a, b: a * b))

Output yang Dihasilkan:

text
13
30

Mari kita telaah baris-barisnya:

  1. Baris 3 (Operasi = Callable[[int, int], int]): alias Operasi dibaca “callable yang menerima dua int lalu mengembalikan int”. List di dalam kurung siku pertama adalah daftar tipe parameter; elemen setelah koma terakhir adalah tipe nilai balik.
  2. Baris 5 (operasi: Operasi): parameter ketiga hitung menerima function apa pun yang cocok dengan bentuk tersebut — lambda, named function, atau method.
  3. Baris 6 (operasi(a, b)): karena checker tahu operasi menerima dua int dan mengembalikan int, pemanggilan ini diperiksa penuh, bukan diperlakukan sebagai Any.

Anotasi Callable tanpa detail — Callable[..., int] — juga sah; ellipsis berarti “parameter apa pun”. Gunakan hanya untuk function yang benar-benar menerima bentuk bebas, karena ellipsis mematikan pemeriksaan argument.

Checker menegakkan kontrak ini sekuat kontrak tipe lainnya. Simpan sebagai tugas.py:

python
from collections.abc import Callable

def jalankan(tugas: Callable[[int], int]) -> int:
    return tugas(21)

print(jalankan("bukan callable"))
bash
python3 -m mypy tugas.py

Output yang Dihasilkan:

text
tugas.py:6: error: Argument 1 to "jalankan" has incompatible type "str"; expected "Callable[[int], int]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Type Alias: Nama Baru untuk Tipe Panjang #

Type alias paling sederhana hanyalah penugasan biasa — sebuah nama baru untuk ekspresi tipe yang sudah ada:

python
HargaMenu = dict[str, int]

menu: HargaMenu = {"kopi": 12000, "teh": 8000}
print(menu["kopi"] + menu["teh"])

Output yang Dihasilkan:

text
20000

Di mata checker — dan di runtime — HargaMenu adalah dict[str, int]; alias bukan tipe baru, hanya nama lain. Karena dibuat dengan penugasan biasa, alias semacam ini dievaluasi langsung saat baris penugasan berjalan (eager). Konsekuensi kecilnya: jika alias merujuk class yang baru didefinisikan di baris bawah, kita harus menuliskan namanya sebagai string.

Statement type pada Python 3.12 #

Python 3.12 memperkenalkan cara deklaratif untuk alias: statement type. Bentuknya menyerupai penugasan, tetapi checker memperlakukannya sebagai definisi tipe yang dievaluasi dengan malas (lazy):

python
type Pelanggan = dict[str, str | int]

pelanggan: Pelanggan = {"nama": "Budi", "umur": 25}
print(pelanggan["nama"])

Output yang Dihasilkan:

text
Budi

Kata kunci type di awal baris adalah pembeda penting: type Pelanggan = ... membuat definisi tipe, sedangkan Pelanggan = ... (tanpa type) membuat objek dictionary biasa di runtime — dua hal yang jauh berbeda maknanya bagi checker.

Keunggulan utama evaluasi malas: alias bisa merujuk dirinya sendiri untuk struktur rekursif, tanpa string dan tanpa from __future__ import annotations. Contoh klasiknya adalah bentuk data JSON:

python
type JSON = int | float | str | bool | None | list[JSON] | dict[str, JSON]

Definisi JSON di atas sah karena seluruh sisi kanan tidak dievaluasi sampai checker membutuhkannya. Dengan alias penugasan lama, list[JSON] di baris yang sama dengan definisinya justru galat NameError.

Satu peringatan penamaan: meskipun alias bisa dinamai bebas, konvensi PEP 8 menyarankan huruf kapital di tiap kata (HargaMenu, Operasi, JSON) agar pembaca langsung mengenali sebuah nama sebagai tipe, bukan variabel biasa.