42. Pengantar Ringan tentang Type Hints (Opsional)
Sepanjang buku ini, kamu sudah menulis kode Python tanpa menyebutkan tipe data apa yang disimpan oleh variabelmu atau tipe apa yang diterima dan dikembalikan oleh fungsi(function)-mu. Python tetap bekerja dengan baik seperti ini—Python adalah bahasa dynamically typed (bertipes dinamis), artinya tipe ditentukan saat runtime ketika programmu dieksekusi. Fleksibilitas ini adalah salah satu kekuatan terbesar Python, memungkinkan kamu menulis kode dengan cepat dan ekspresif.
Namun, ketika program makin besar dan kompleks, fleksibilitas ini kadang bisa membuat kode lebih sulit dipahami dan dipelihara. Saat kamu melihat fungsi seperti def process_data(items):, kamu mungkin bertanya-tanya: Data seperti apa yang ada di items? List berisi string? Dictionary? Atau sesuatu yang sama sekali berbeda?
Type hints (juga disebut type annotations) menyediakan cara untuk mendokumentasikan tipe yang diharapkan di kodemu. Mereka adalah tambahan opsional pada Python yang bisa membuat kodemu lebih jelas, membantu menangkap error lebih awal, dan mengaktifkan fitur IDE yang kuat—semuanya tanpa mengubah cara Python benar-benar menjalankan kodemu.
Bab ini memperkenalkan type hints secara pelan-pelan, menunjukkan apa itu, kenapa ada, dan bagaimana menggunakannya secara efektif. Karena type hints bersifat opsional dan tidak memengaruhi cara Python mengeksekusi kodemu, seluruh bab ini ditandai sebagai opsional. Kamu bisa menulis program Python yang bagus tanpa pernah memakai type hints. Namun memahaminya akan membantumu membaca kode Python modern dan memutuskan kapan mereka bisa bermanfaat untuk proyekmu sendiri.
42.1) Kenapa Type Hints Ditambahkan ke Python
Python dirancang sebagai bahasa bertipe dinamis sejak awal. Selama puluhan tahun, programmer Python menulis kode tanpa informasi tipe apa pun, dan ini bekerja dengan sangat baik untuk banyak sekali proyek. Jadi kenapa type hints ditambahkan ke Python pada 2015 (dengan Python 3.5)?
Tantangan Codebase yang Besar
Ketika Python makin populer untuk aplikasi skala besar, tim-tim menemui tantangan:
# Dalam codebase yang besar, fungsi ini mengharapkan apa dan mengembalikan apa?
def calculate_discount(customer, items, code):
# ... 50 baris kode ...
return resultTanpa membaca seluruh isi fungsi atau dokumentasinya, kamu tidak bisa tahu:
- Apakah
customeradalah dictionary, objek kustom, atau sesuatu yang lain? - Apakah
itemsadalah list, tuple, atau set? - Tipe
codeitu apa—string, integer? - Fungsi ini mengembalikan apa—angka, dictionary, atau mungkin
None?
Dalam program kecil, ambiguitas ini masih bisa dikelola. Kamu bisa dengan mudah melihat bagaimana fungsi itu digunakan di tempat lain. Tapi dalam codebase dengan ribuan fungsi di puluhan file, ini menjadi sulit.
Solusinya: Type Hints yang Opsional
Para pembuat Python memutuskan untuk menambahkan sistem opsional untuk mendokumentasikan tipe. Kata kuncinya adalah "opsional"—type hints sepenuhnya sukarela. Kamu bisa memakainya saat membantu, mengabaikannya saat tidak membantu, dan mencampur kode yang dianotasi dan yang tidak dianotasi dengan bebas.
Ini contoh sederhana untuk menunjukkan sintaks dasarnya:
# Tanpa type hints
def add(a, b):
return a + b
# Dengan type hints
def add(a: int, b: int) -> int:
return a + bSintaksnya langsung:
- Titik dua (
:) setelah parameter menunjukkan tipe yang seharusnya:a: int - Panah (
->) sebelum titik dua menunjukkan tipe yang dikembalikan fungsi:-> int
Sekarang mari lihat ini dengan contoh kita sebelumnya:
def calculate_discount(customer: dict, items: list, code: str) -> float:
# ... 50 lines of code ...
return resultSekarang langsung jelas: customer adalah dictionary, items adalah list, code adalah string, dan fungsi mengembalikan float.
Jangan khawatir kalau sintaks ini terlihat asing—kita akan membahasnya secara detail di bagian 42.3-42.6. Untuk sekarang, cukup perhatikan bagaimana kamu bisa tahu sekilas apa yang diharapkan fungsi dan apa yang dikembalikannya.
Dengan atau tanpa type hints, fungsi bekerja dengan cara yang persis sama—Python tidak memeriksa tipe-tipe ini saat runtime. (Kita akan membahas poin penting ini secara detail di bagian 42.2)
Pendekatan Bertahap yang Pragmatik
Sistem type hint Python dirancang agar:
- Opsional: Kamu tidak pernah wajib memakai type hints
- Bertahap: Kamu bisa menambahkan hints ke sebagian kode dan tidak pada bagian lain
- Tidak mengganggu: Hints tidak mengubah cara Python mengeksekusi kodemu
- Ramah alat: Alat eksternal bisa memeriksa hints, tapi Python sendiri mengabaikannya saat runtime
Pendekatan pragmatis ini membuat Python tetap fleksibel sambil memberikan manfaat bagi yang menginginkannya.
42.2) Aturan Emas: Tidak Ada Penegakan Saat Runtime
Hal terpenting untuk dipahami tentang type hints adalah ini: Python tidak menegakkan type hints saat runtime. Mereka murni bersifat informasional. Mari lihat apa arti kenyataan yang mengejutkan ini dalam praktik.
Type Hints Tidak Mencegah Tipe yang Salah
Pertimbangkan fungsi ini dengan type hints:
def greet(name: str) -> str:
return f"Hello, {name}!"
# Ini tetap bekerja, meskipun 42 bukan string
result = greet(42)
print(result) # Output: Hello, 42!Type hint dengan jelas mengatakan name seharusnya string, tapi Python dengan santai menerima integer 42 dan menjalankan fungsi. Python tidak memeriksa type hint—Python hanya memakai nilai yang kamu berikan.
Ini pada dasarnya berbeda dari bahasa seperti Java atau C++, di mana compiler memeriksa tipe sebelum menjalankan kode dan menolak menjalankannya jika ada ketidakcocokan tipe. Pendekatan Python lebih permisif: Python mempercayaimu untuk memberikan tipe yang benar, tapi tidak memaksamu melakukannya.
Masalahnya: Risiko Tipe Dinamis Tetap Ada
Ini tantangan sebenarnya: bahkan dengan type hints, dynamic typing Python berarti kamu tetap bisa membuat kesalahan tipe yang baru muncul saat runtime:
def calculate_total(prices: list) -> float:
"""Calculate the sum of prices."""
return sum(prices)
# Ini berjalan dengan baik
print(calculate_total([10.99, 5.50, 3.25])) # Output: 19.74
# Tapi ini gagal saat runtime!
print(calculate_total("not a list")) # TypeError: 'str' object is not iterableType hint dengan jelas mengatakan prices seharusnya list, tapi Python tidak menghentikanmu untuk mengoper string. Error baru muncul ketika kode benar-benar berjalan dan mencoba memakai sum() pada string.
Ini bikin frustrasi! Kita menambahkan type hints untuk menangkap masalah-masalah ini, tapi risiko dynamic typing masih ada. Type error bisa bersembunyi di kodemu sampai runtime, dan berpotensi muncul di production saat pengguna melakukan sesuatu yang tidak terduga.
Jadi kalau type hints tidak mencegah runtime error, apa gunanya memakainya?
Jadi Type Hints Itu Untuk Apa?
Type hints mungkin tidak mengubah perilaku runtime Python, tapi mereka punya tujuan penting—memberikan informasi untuk manusia dan tools, bukan untuk Python itu sendiri:
- Dokumentasi: Mereka memberi tahu tipe apa yang diharapkan dan dikembalikan sebuah fungsi
- Dukungan IDE: Editor bisa memakai hints untuk autocomplete dan menampilkan peringatan
- Analisis statis: Tools eksternal (seperti mypy) bisa memeriksa kodemu untuk type error sebelum kamu menjalankannya
- Memahami kode: Mereka membuat codebase besar lebih mudah dibaca dan dipelihara
Anggap type hints sebagai komentar yang bisa dipahami tools. Mereka tidak mengubah cara Python berjalan, tapi membantu kamu menulis kode yang lebih baik.
Tapi bagaimana ini benar-benar membantu kita menangkap runtime error yang baru saja kita lihat?
Solusinya: Type Hints + Dukungan IDE
Di sinilah type hints benar-benar bersinar. Walaupun Python tidak menegakkannya saat runtime, IDE-mu bisa menangkap kesalahan bahkan sebelum kamu menjalankan kode:
def add_numbers(a: int, b: int) -> int:
"""Tambahkan dua angka."""
return a + b
# IDE-mu akan menampilkan peringatan di sini (sebelum kamu menjalankan kode)
result = add_numbers("Hello", "World") # IDE: Warning - expected int, got strEditor kode melihat type hints dan bisa memperingatkanmu tentang ketidakcocokan tipe saat kamu mengetik, jauh sebelum kamu menjalankan kode. Ini menangkap banyak bug saat development, bukan di production.
Pengembangan Python modern biasanya bekerja seperti ini:
- Kamu menulis kode dengan type hints
- IDE-mu menampilkan peringatan saat tipe tidak cocok
- Kamu memperbaiki masalah sebelum menjalankan kode
- Runtime error akibat ketidakcocokan tipe jadi jauh lebih jarang
Type hint tidak mencegah error saat runtime, tapi IDE-mu memakainya untuk mencegahmu menulis kode yang bug sejak awal!
Yang Terbaik dari Dua Dunia
Type hints memberi Python yang terbaik dari dua dunia—menangkap sebagian besar error lebih awal sambil tetap menjaga fleksibilitas:
Keamanan saat development: IDE-mu dan type checker menangkap sebagian besar type error saat development, jadi kamu menemukan bug lebih awal.
def process(data: list) -> list:
return [x * 2 for x in data]
# Kalau kamu tidak sengaja mengoper string:
process("hello") # IDE warns: expected list, got str
# Kamu memperbaikinya sebelum menjalankan kode!Fleksibilitas runtime: Python tetap menjalankan kode dengan tipe yang tidak cocok, yang bisa berguna untuk prototyping cepat atau saat kamu memang sengaja ingin menerima banyak tipe.
def add_numbers(a: int, b: int) -> int:
return a + b
# Python akan menjalankan ini, meskipun tipe tidak cocok
print(add_numbers(5.5, 3.2)) # Output: 8.7 (works!)
print(add_numbers("Hi", " there")) # Output: Hi there (also works!)Fleksibilitas ini berarti kamu tidak terkunci pada sistem tipe yang kaku. Saat kamu perlu melanggar aturan (untuk testing, prototyping, atau use case yang sah), Python membiarkanmu. Tapi saat kamu menulis kode production, IDE-mu menjaga kamu tetap aman.
Ingat Aturan Emasnya: Type hints tidak mengubah perilaku runtime Python—mereka hanya memberi kamu dan tools-mu informasi yang dibutuhkan untuk menangkap masalah lebih awal. Kamu tetap perlu hati-hati, tapi sekarang kamu punya sekutu kuat yang menjaga punggungmu.
42.3) Menganotasi Fungsi: Parameter dan Nilai Kembalian
Penggunaan type hints yang paling umum adalah menganotasi parameter fungsi(function) dan nilai kembalian. Ini memberi tahu pembaca (dan tools) tipe apa yang diharapkan dan dihasilkan oleh fungsi. Mari mulai dari kasus paling sederhana dan bertahap meningkat.
Dasar-Dasarnya: Anotasi Parameter
Untuk menambahkan type hint ke parameter, letakkan titik dua setelah nama parameter, diikuti tipe:
def greet(name: str):
"""Sapa seseorang berdasarkan namanya."""
return f"Hello, {name}!"
# Penggunaan
message = greet("Alice")
print(message) # Output: Hello, Alice!Sintaks name: str berarti "parameter name seharusnya string." Kamu bisa menambahkan type hints ke beberapa parameter:
def calculate_area(width: float, height: float):
"""Hitung luas sebuah persegi panjang."""
return width * height
# Penggunaan
area = calculate_area(5.0, 3.0)
print(area) # Output: 15.0Di sini, width dan height sama-sama dianotasi sebagai float. Fungsinya bekerja sama seperti sebelumnya—type hints tidak mengubah perilaku—tapi sekarang IDE-mu tahu tipe apa yang diharapkan.
Menambahkan Anotasi Tipe Kembalian
Untuk menentukan tipe apa yang dikembalikan sebuah fungsi, tambahkan -> type setelah daftar parameter dan sebelum titik dua:
def get_full_name(first: str, last: str) -> str:
"""Gabungkan nama depan dan nama belakang."""
return f"{first} {last}"
# Penggunaan
name = get_full_name("John", "Doe")
print(name) # Output: John Doe-> str berarti "fungsi ini mengembalikan string." Anotasi tipe kembalian sangat membantu ketika tipe kembalian tidak jelas dari nama fungsi:
def is_adult(age: int) -> bool:
"""Periksa apakah seseorang adalah orang dewasa (18 tahun atau lebih)."""
return age >= 18
# Penggunaan
adult = is_adult(25)
print(adult) # Output: TrueTanpa melihat implementasinya, kamu langsung tahu fungsi ini mengembalikan nilai boolean.
Menggabungkannya: Fungsi yang Lengkap
Kebanyakan fungsi akan punya anotasi untuk parameter dan tipe kembalian. Ini contoh seperti apa fungsi yang dianotasi penuh:
def calculate_discount(price: float, discount_percent: float) -> float:
"""Hitung harga setelah diskon."""
discount_amount = price * (discount_percent / 100)
return price - discount_amount
# Penggunaan
original_price = 100.0
discount = 20.0
final_price = calculate_discount(original_price, discount)
print(f"Final price: ${final_price:.2f}") # Output: Final price: $80.00Signature fungsi ini memberi tahu kamu semua yang perlu kamu tahu:
- Menerima dua parameter
float:pricedandiscount_percent - Mengembalikan nilai
float - Kamu tidak perlu membaca implementasinya untuk memahami cara menggunakan fungsi ini
Mari lihat contoh lain dengan tipe yang berbeda:
def repeat_message(message: str, times: int) -> str:
"""Ulangi sebuah pesan sejumlah kali tertentu."""
return message * times
# Penggunaan
repeated = repeat_message("Hello! ", 3)
print(repeated) # Output: Hello! Hello! Hello! Type hints membuat jelas bahwa kamu mengoper string dan integer, dan mendapatkan string sebagai hasil.
Bekerja dengan Nilai Default
Saat sebuah parameter punya nilai default, letakkan type hint di antara nama parameter dan nilai default:
def create_greeting(name: str, formal: bool = False) -> str:
"""Buat pesan sapaan."""
if formal:
return f"Good day, {name}."
return f"Hi, {name}!"
# Penggunaan
print(create_greeting("Alice")) # Output: Hi, Alice!
print(create_greeting("Bob", formal=True)) # Output: Good day, Bob.Sintaks formal: bool = False berarti "formal adalah boolean dengan nilai default False."
Kamu bisa punya beberapa parameter dengan default, semuanya dianotasi:
def format_price(amount: float, currency: str = "USD", decimals: int = 2) -> str:
"""Format harga dengan simbol mata uang."""
if currency == "USD":
symbol = "$"
elif currency == "EUR":
symbol = "€"
else:
symbol = currency
return f"{symbol}{amount:.{decimals}f}"
# Penggunaan
print(format_price(99.99)) # Output: $99.99
print(format_price(99.99, "EUR")) # Output: €99.99
print(format_price(99.995, "USD", 3)) # Output: $99.995Setiap parameter menampilkan tipe dan nilai default-nya dengan jelas, membuat fungsi mudah dipahami dan digunakan.
Kasus Khusus: Fungsi yang Tidak Mengembalikan Nilai
Beberapa fungsi hanya melakukan aksi (seperti mencetak atau menulis ke file) tanpa mengembalikan nilai. Untuk memperjelas bahwa fungsi-fungsi ini tidak mengembalikan apa pun, gunakan -> None:
def print_report(title: str, data: list) -> None:
"""Cetak laporan yang terformat."""
print(f"=== {title} ===")
for item in data:
print(f" - {item}")
# Tidak ada statement return, jadi secara implisit mengembalikan None
# Penggunaan
print_report("Sales Data", [100, 150, 200])Output:
=== Sales Data ===
- 100
- 150
- 200Anotasi -> None secara eksplisit menunjukkan bahwa fungsi ini tidak mengembalikan nilai yang bermakna.
Kenapa memakai -> None?
- Kejelasan: Ini membuat maksudmu eksplisit—fungsi ini untuk aksi, bukan hasil
- Dukungan IDE: IDE-mu bisa memperingatkan jika kamu tidak sengaja mencoba memakai nilai return
42.4) Anotasi Variabel Sederhana
Walaupun type hints paling sering digunakan pada fungsi, kamu juga bisa menganotasi variabel. Mari lihat bagaimana ini bekerja dan kapan ini benar-benar berguna.
Sintaks Dasar Anotasi Variabel
Untuk menganotasi variabel, gunakan sintaks titik dua yang sama seperti pada parameter fungsi:
# Menganotasi variabel
name: str = "Alice"
age: int = 30
height: float = 5.7
is_student: bool = True
print(f"{name} is {age} years old") # Output: Alice is 30 years oldSintaks name: str = "Alice" berarti "variabel name adalah string dan memiliki nilai 'Alice'." Anotasi ini tidak mengubah cara variabel bekerja—ini murni informasional.
Anotasi Variabel Sering Dilewati
Dalam praktiknya, anotasi variabel jarang digunakan. Alasannya sederhana: Python bisa menyimpulkan tipe dari nilainya, jadi anotasi biasanya berlebihan:
# Anotasi-anotasi ini tidak perlu
name: str = "Alice" # Jelas string
count: int = 0 # Jelas int
prices: list = [10.99, 5.50] # Jelas list
settings: dict = {} # Jelas dict
# Cukup tulis ini saja
name = "Alice"
count = 0
prices = [10.99, 5.50]
settings = {}Saat kamu menulis name = "Alice", baik kamu maupun IDE-mu langsung tahu itu string. Anotasi tidak menambahkan informasi yang berguna.
Di kode Python dunia nyata, kamu jarang akan melihat anotasi variabel. Ini normal dan memang seperti itu. Anotasi fungsi jauh lebih penting dan lebih umum.
Satu Kasus yang Berguna: Mendeklarasikan Variabel Sebelum Assignment
Ada satu situasi di mana anotasi variabel benar-benar berguna: ketika kamu perlu mendeklarasikan variabel sebelum memberikan nilainya.
def calculate_statistics(numbers: list) -> dict:
"""Hitung statistik dasar dari sebuah list angka."""
# Deklarasikan variabel sebelum menggunakannya
total: float
count: int
average: float
# Sekarang beri nilai
total = sum(numbers)
count = len(numbers)
average = total / count if count > 0 else 0.0
return {
"total": total,
"count": count,
"average": average
}
# Penggunaan
result = calculate_statistics([10, 20, 30, 40])
print(f"Average: {result['average']}") # Output: Average: 25.0Tanpa anotasi, kamu tidak bisa mendeklarasikan variabel tanpa sekaligus memberi nilai. Anotasi memungkinkan kamu menentukan tipe terlebih dulu, yang bisa membuat struktur kode lebih jelas.
Ini adalah use case praktis utama untuk anotasi variabel.
Ingat: Variabel Bisa Di-assign Ulang ke Tipe yang Berbeda
Bahkan dengan anotasi tipe, kamu bisa meng-assign ulang variabel ke tipe lain:
# Mulai dengan string
value: str = "hello"
print(value) # Output: hello
# Assign ulang ke tipe berbeda - Python mengizinkan ini
value = 42
print(value) # Output: 42
# Perubahan tipe lain - tetap diizinkan
value = [1, 2, 3]
print(value) # Output: [1, 2, 3]IDE-mu atau static type checker akan memperingatkanmu tentang perubahan tipe ini, tapi Python sendiri tidak mencegahnya. Type hints membimbingmu menuju konsistensi tapi tidak menegakkannya saat runtime.
42.5) Menangani "None": Tipe Opsional dan Operator |
Salah satu pola paling umum di Python adalah fungsi yang mungkin mengembalikan nilai atau mungkin mengembalikan None. Misalnya, mencari item bisa berhasil (mengembalikan itemnya) atau gagal (mengembalikan None). Type hints menyediakan cara yang jelas untuk mengekspresikan pola ini.
Masalahnya: Fungsi yang Mungkin Mengembalikan None
Pertimbangkan fungsi ini yang mencari user:
def find_user_by_email(email: str) -> dict:
"""Temukan user berdasarkan alamat email."""
users = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
for user in users:
if user["email"] == email:
return user
return None # Ketidakcocokan tipe! Ini bertentangan dengan hint -> dict
# Penggunaan
user = find_user_by_email("alice@example.com")
if user:
print(f"Found: {user['name']}") # Output: Found: Alice
else:
print("User not found")Type hint -> dict menyesatkan karena fungsi bisa mengembalikan None. Static type checker akan memperingatkan bahwa mengembalikan None tidak cocok dengan tipe kembalian dict yang dideklarasikan.
Solusi: Menggunakan Operator | untuk Tipe Opsional
Python 3.10 memperkenalkan operator | untuk type hints, yang berarti "atau." Kamu bisa memakainya untuk menunjukkan bahwa fungsi mungkin mengembalikan satu tipe atau tipe lain:
def find_user_by_email(email: str) -> dict | None:
"""Temukan user berdasarkan alamat email. Mengembalikan None jika tidak ditemukan."""
users = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
for user in users:
if user["email"] == email:
return user
return None
# Penggunaan
user = find_user_by_email("alice@example.com")
if user:
print(f"Found: {user['name']}") # Output: Found: Alice
missing = find_user_by_email("charlie@example.com")
if missing is None:
print("User not found") # Output: User not foundType hint -> dict | None berarti "fungsi ini mengembalikan dictionary atau None." Ini menggambarkan perilaku fungsi dengan akurat.
Catatan: Di kode Python yang lebih lama (sebelum 3.10), kamu mungkin melihat Optional[dict] dari modul typing alih-alih dict | None. Artinya sama, tapi | adalah sintaks modern yang lebih disukai.
Menggunakan | dengan Banyak Tipe
Kamu bisa memakai | untuk menunjukkan lebih dari dua tipe yang mungkin:
def parse_value(text: str) -> int | float | None:
"""Parse string menjadi angka. Mengembalikan None jika parsing gagal."""
try:
# Coba parse sebagai integer dulu
if '.' not in text:
return int(text)
# Kalau tidak, parse sebagai float
return float(text)
except ValueError:
return None
# Penggunaan
print(parse_value("42")) # Output: 42 (int)
print(parse_value("3.14")) # Output: 3.14 (float)
print(parse_value("invalid")) # Output: NoneType hint -> int | float | None berarti fungsi bisa mengembalikan integer, float, atau None.
Memeriksa None: Praktik Terbaik
Saat fungsi bisa mengembalikan None, selalu periksa None sebelum memakai hasilnya. Kalau tidak, kamu berisiko error ketika mencoba menggunakan None seolah-olah itu tipe yang diharapkan:
def get_user_age(user_id: int) -> int | None:
"""Ambil umur user. Mengembalikan None jika user tidak ditemukan."""
users = {1: 25, 2: 30, 3: 35}
return users.get(user_id)
# Selalu periksa None sebelum memakai nilainya
age = get_user_age(1)
if age is not None:
print(f"User is {age} years old") # Output: User is 25 years old
if age >= 18:
print("User is an adult") # Output: User is an adult
else:
print("User not found")
# Untuk user yang tidak ada
age = get_user_age(999)
if age is None:
print("User not found") # Output: User not foundKuncinya adalah memakai if age is not None: atau if age is None: untuk memeriksa secara eksplisit sebelum menggunakan nilainya.
Parameter Opsional dengan | None
Kamu juga bisa memakai | pada parameter, sering dikombinasikan dengan nilai default:
def format_name(first: str, middle: str | None = None, last: str = "") -> str:
"""Format nama lengkap. Nama tengah opsional."""
if middle and last:
return f"{first} {middle} {last}"
elif last:
return f"{first} {last}"
return first
# Penggunaan
print(format_name("John", "Q", "Doe")) # Output: John Q Doe
print(format_name("Jane", None, "Smith")) # Output: Jane Smith
print(format_name("Prince")) # Output: PrinceType hint middle: str | None = None menunjukkan bahwa middle bisa berupa string atau None, dengan None sebagai nilai default. Ini pola yang umum untuk parameter opsional.
42.6) Membaca Type Hints yang Umum: list, dict, tuple
Saat kamu membaca kode Python yang ditulis orang lain, kamu akan menemukan type hints untuk koleksi seperti list, dictionary, dan tuple. Python modern menyediakan cara yang jelas untuk menyatakan bukan hanya bahwa sesuatu itu list, tapi juga tipe item apa yang ada di dalam list tersebut.
Catatan: Sintaks yang ditunjukkan di sini (list[int], dict[str, int], dll.) bekerja di Python 3.9+. Di kode yang lebih lama, kamu mungkin melihat List[int] dan Dict[str, int] (huruf besar) dari modul typing—mereka bekerja dengan cara yang sama.
Type Hints Koleksi Dasar
Type hints koleksi yang paling sederhana hanya menyebutkan tipe koleksinya:
def print_items(items: list) -> None:
"""Cetak semua item dalam sebuah list."""
for item in items:
print(item)
def get_user_settings() -> dict:
"""Ambil pengaturan user sebagai dictionary."""
return {"theme": "dark", "notifications": True}
def get_position() -> tuple:
"""Ambil posisi x, y."""
return (10, 20)Hints ini memberi tahu tipe koleksi tapi tidak memberi tahu isi di dalamnya.
List: Menentukan Tipe Item
Untuk menentukan tipe item yang ada dalam list, gunakan tanda kurung siku:
def calculate_total(prices: list[float]) -> float:
"""Hitung total dari semua harga."""
return sum(prices)
# Penggunaan
total = calculate_total([10.99, 5.50, 3.25])
print(f"Total: ${total:.2f}") # Output: Total: $19.74Type hint list[float] berarti "list yang berisi float." Ini lebih informatif daripada hanya list.
Ini contoh lain dengan string:
def format_names(names: list[str]) -> str:
"""Format list nama sebagai string yang dipisahkan koma."""
return ", ".join(names)
# Penggunaan
students = ["Alice", "Bob", "Charlie"]
print(format_names(students)) # Output: Alice, Bob, CharlieType hint list[str] berarti "list yang berisi string."
Dictionary: Menentukan Tipe Key dan Value
Untuk dictionary, tentukan tipe key dan tipe value:
def get_student_grades() -> dict[str, int]:
"""Ambil nama siswa yang dipetakan ke nilai mereka."""
return {
"Alice": 95,
"Bob": 87,
"Charlie": 92
}
# Penggunaan
grades = get_student_grades()
for name, grade in grades.items():
print(f"{name}: {grade}")Output:
Alice: 95
Bob: 87
Charlie: 92Type hint dict[str, int] berarti "dictionary dengan key string dan value integer."
Ini contoh di mana value bisa punya beberapa tipe:
def get_user_data(user_id: int) -> dict[str, str | int]:
"""Ambil data user. Value bisa berupa string atau integer."""
return {
"name": "Alice",
"email": "alice@example.com",
"age": 30,
"id": 12345
}
# Penggunaan
user = get_user_data(1)
print(f"{user['name']} is {user['age']} years old") # Output: Alice is 30 years oldType hint dict[str, str | int] berarti "dictionary dengan key string dan value yang bisa berupa string atau integer."
Tuple: Panjang Tetap dan Variabel
Tuple berbeda dari list karena sering punya struktur yang tetap. Kamu bisa menentukan tipe untuk setiap posisi:
def get_user_info(user_id: int) -> tuple[str, int, bool]:
"""
Ambil informasi user sebagai tuple.
Returns: (name, age, is_active)
"""
return ("Alice", 30, True)
# Penggunaan
name, age, active = get_user_info(1)
print(f"{name}, {age}, active: {active}") # Output: Alice, 30, active: TrueType hint tuple[str, int, bool] berarti "tuple dengan tepat tiga elemen: string, integer, dan boolean, dalam urutan itu."
Untuk tuple dengan panjang variabel dengan item bertipe sama, gunakan ellipsis (...):
# Tuple panjang tetap: tepat 2 float
def get_2d_point() -> tuple[float, float]:
"""Ambil koordinat 2D (x, y)."""
return (10.5, 20.3)
# Tuple panjang variabel: sejumlah float berapa pun
def get_coordinates() -> tuple[float, ...]:
"""Ambil koordinat. Bisa 2D, 3D, atau dimensi apa pun."""
return (10.5, 20.3, 15.7) # 3D dalam kasus ini
# Penggunaan
point = get_2d_point()
coords = get_coordinates()
print(f"2D point: {point}") # Output: 2D point: (10.5, 20.3)
print(f"Coordinates: {coords}") # Output: Coordinates: (10.5, 20.3, 15.7)Type hint tuple[float, ...] berarti "tuple yang berisi sejumlah float berapa pun." ... berarti "sejumlah berapa pun dari tipe ini."
Koleksi Bersarang
Kamu bisa membuat type hints bersarang untuk struktur data yang kompleks. Mari mulai dengan contoh sederhana:
def get_scores_by_student() -> dict[str, list[int]]:
"""Ambil nilai ujian untuk setiap siswa."""
return {
"Alice": [95, 87, 92],
"Bob": [88, 91, 85],
"Charlie": [90, 88, 94]
}
# Penggunaan
scores = get_scores_by_student()
for name, tests in scores.items():
average = sum(tests) / len(tests)
print(f"{name}: {average:.1f}")Output:
Alice: 91.3
Bob: 88.0
Charlie: 90.7Type hint dict[str, list[int]] berarti "dictionary dengan key string dan value berupa list integer."
Ini contoh yang lebih kompleks:
def get_student_records() -> list[dict[str, str | int]]:
"""Ambil list berisi catatan siswa."""
return [
{"name": "Alice", "age": 20, "major": "CS"},
{"name": "Bob", "age": 21, "major": "Math"},
{"name": "Charlie", "age": 19, "major": "Physics"}
]
# Penggunaan
students = get_student_records()
for student in students:
print(f"{student['name']}, {student['age']}, {student['major']}")Output:
Alice, 20, CS
Bob, 21, Math
Charlie, 19, PhysicsType hint list[dict[str, str | int]] berarti "list berisi dictionary, di mana setiap dictionary punya key string dan value yang bisa berupa string atau integer."
Membaca Type Hints: Referensi Cepat
Saat kamu menemui type hints di kode, ini cara membacanya:
Koleksi:
list[int]- "list berisi integer"dict[str, float]- "dictionary dengan key string dan value float"tuple[str, int]- "tuple dengan tepat dua item: string, lalu integer"tuple[float, ...]- "tuple yang berisi sejumlah float berapa pun"
Opsional dan Banyak Tipe:
int | None- "integer atau None"str | int | float- "string, integer, atau float"
Bersarang:
list[dict[str, int]]- "list berisi dictionary (setiap dict punya key string dan value integer)"dict[str, list[float]]- "dictionary dengan key string dan value berupa list float"
Catatan: Di kode yang lebih lama (Python < 3.10), kamu mungkin melihat Union[int, str] alih-alih int | str, atau Optional[int] alih-alih int | None. Mereka artinya sama.