Python

Python Type Hints: Type Annotations

Pelajari type hints, typing module, generics, protocols, Union types, Optional, dan static type checking dengan mypy untuk kode Python yang lebih robust

1. Pengenalan Type Hints

Type hints (atau type annotations) adalah fitur Python yang memungkinkan Anda mendeklarasikan tipe data untuk variabel, parameter fungsi, dan return value. Diperkenalkan di Python 3.5 melalui PEP 484, type hints membantu menulis kode yang lebih jelas, terdokumentasi, dan aman.

Penting untuk dipahami bahwa type hints tidak memaksa tipe data pada runtime seperti bahasa static-typed (Java, C++). Python tetap dynamic-typed β€” type hints hanya berfungsi sebagai dokumentasi dan bisa diperiksa oleh tools seperti mypy sebelum kode dijalankan.

Mengapa Type Hints Penting?

Manfaat Penjelasan
DokumentasiTipe data jelas terlihat di signature fungsi
AutocompleteIDE memberikan suggestions yang lebih akurat
Bug PreventionDeteksi error sebelum runtime dengan mypy
RefactoringLebih aman dan mudah saat mengubah kode
ReadabilityKode lebih mudah dipahami oleh developer lain
ToolingDidukung oleh semua IDE modern (VS Code, PyCharm)
Diagram: Type Hints Workflow
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            TYPE HINTS WORKFLOW                       β”‚
β”‚                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚  β”‚  Tulis   │───►│  mypy    │───►│  Runtime β”‚       β”‚
β”‚  β”‚  Kode +  β”‚    β”‚  Check   β”‚    β”‚  (Normal)β”‚       β”‚
β”‚  β”‚  Type    β”‚    β”‚          β”‚    β”‚          β”‚       β”‚
β”‚  β”‚  Hints   β”‚    β”‚  βœ… Pass β”‚    β”‚  Python  β”‚       β”‚
β”‚  β”‚          β”‚    β”‚  ❌ Errorβ”‚    β”‚  ignore  β”‚       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
β”‚                                                      β”‚
β”‚  Type hints = dokumentasi + static analysis          β”‚
β”‚  BUKAN enforcement pada runtime                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Sintaks Dasar Type Hints

Variable Annotations

Python β€” Variable Annotations
# Type hints untuk variabel
nama: str = "Budi Santoso"
umur: int = 25
tinggi: float = 175.5
is_active: bool = True

# Tanpa nilai default
alamat: str  # Hanya deklarasi, belum diisi

# Type hints TIDAK mengubah perilaku Python
x: int = "ini string"  # Python tidak error, tapi mypy akan warning!
print(x)  # "ini string" β€” tetap jalan

# Collection types dasar
angka: list[int] = [1, 2, 3, 4, 5]
pasangan: dict[str, int] = {"a": 1, "b": 2}
himpunan: set[str] = {"apel", "mangga"}
koordinat: tuple[float, float] = (3.14, 2.71)
campuran: tuple[int, str, float] = (1, "dua", 3.0)

# Type hint di class
class Pengguna:
    nama: str
    umur: int
    email: str

    def __init__(self, nama: str, umur: int, email: str) -> None:
        self.nama = nama
        self.umur = umur
        self.email = email

Function Annotations

Python β€” Function Annotations
# Type hints untuk fungsi
# Sintaks: def nama(param: tipe) -> return_tipe:

def sapa(nama: str) -> str:
    return f"Halo, {nama}!"

def tambah(a: int, b: int) -> int:
    return a + b

def hitung_luas(panjang: float, lebar: float) -> float:
    return panjang * lebar

# Tanpa return value β†’ gunakan None
def cetak_pesan(pesan: str) -> None:
    print(pesan)

# Default parameter dengan type hints
def buat_profil(
    nama: str,
    umur: int = 18,
    kota: str = "Jakarta",
    aktif: bool = True
) -> dict[str, str | int | bool]:
    return {
        "nama": nama,
        "umur": umur,
        "kota": kota,
        "aktif": aktif
    }

# Fungsi dengan multiple return types
def bagi(a: float, b: float) -> float | None:
    if b == 0:
        return None
    return a / b

# Lambda dengan type hints (tidak didukung langsung)
# Gunakan variabel bertipe:
kali_dua: Callable[[int], int] = lambda x: x * 2

3. Modul Typing

Modul typing menyediakan tipe-tipe lanjutan yang tidak tersedia sebagai built-in types. Ini sangat penting untuk type hints yang lebih ekspresif.

Python β€” Typing Module
from typing import (
    Optional, Union, Any, NoReturn,
    List, Dict, Tuple, Set,  # Deprecated di Python 3.9+
    Callable, Iterator, Generator,
    TypeVar, Generic, ClassVar,
    Final, Literal, TypeAlias
)

# === Optional β€” nilai bisa tipe atau None ===
def cari_user(user_id: int) -> Optional[str]:
    """Mengembalikan nama user atau None jika tidak ditemukan"""
    database = {1: "Budi", 2: "Ani", 3: "Dimas"}
    return database.get(user_id)

hasil = cari_user(1)     # str atau None
hasil2 = cari_user(99)   # None

# Optional[str] sama dengan Union[str, None]

# === Union β€” beberapa tipe yang mungkin ===
def proses_input(data: Union[str, int, float]) -> str:
    """Menerima input berbagai tipe"""
    return str(data).upper()

# Python 3.10+ bisa pakai | operator:
def proses_input_modern(data: str | int | float) -> str:
    return str(data).upper()

# === Any β€” tipe apapun diterima ===
def debug_print(value: Any) -> None:
    """Mencetak nilai apapun untuk debugging"""
    print(f"[DEBUG] {type(value).__name__}: {value}")

# === Callable β€” fungsi sebagai parameter ===
from typing import Callable

def jalankan_dua_kali(
    func: Callable[[int], int],
    nilai: int
) -> int:
    """Menjalankan fungsi dua kali secara berurutan"""
    return func(func(nilai))

hasil = jalankan_dua_kali(lambda x: x + 1, 5)
print(hasil)  # 7 (5+1+1)

# Callable dengan signature lengkap
# Callable[[param_types], return_type]
handler: Callable[[str, int], bool]

# === Iterator dan Generator types ===
from typing import Iterator, Generator

def angka_gen(n: int) -> Iterator[int]:
    for i in range(n):
        yield i

def fibonacci() -> Generator[int, None, None]:
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# === Literal β€” nilai spesifik yang diterima ===
def set_mode(mode: Literal["read", "write", "append"]) -> None:
    print(f"Mode: {mode}")

set_mode("read")    # βœ… OK
set_mode("delete")  # ❌ mypy akan error!

# === Final β€” konstanta yang tidak boleh diubah ===
MAX_SIZE: Final = 100
APP_NAME: Final[str] = "BeebaneLabs"

# MAX_SIZE = 200  ← mypy akan error!

4. Tipe Data Kompleks

Python β€” Complex Types
from typing import Optional, Union

# === Nested collections ===
# List of dicts
siswa_list: list[dict[str, str | int]] = [
    {"nama": "Budi", "umur": 25},
    {"nama": "Ani", "umur": 22},
]

# Dict of lists
jadwal: dict[str, list[str]] = {
    "senin": ["Matematika", "Fisika"],
    "selasa": ["Kimia", "Biologi"],
}

# Dict of dicts
database: dict[str, dict[str, str | int]] = {
    "user1": {"nama": "Budi", "umur": 25},
    "user2": {"nama": "Ani", "umur": 22},
}

# Nested list (matrix)
matriks: list[list[int]] = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

# === TypedDict β€” dictionary dengan struktur tetap ===
from typing import TypedDict

class UserDict(TypedDict):
    nama: str
    umur: int
    email: str
    aktif: bool

# Sekarang dict harus punya key yang sesuai
user: UserDict = {
    "nama": "Budi",
    "umur": 25,
    "email": "budi@email.com",
    "aktif": True
}

# TypedDict dengan Optional keys
class ProductDict(TypedDict, total=False):
    nama: str          # Required (default)
    harga: int         # Required (default)
    deskripsi: str     # Optional (total=False)
    rating: float      # Optional (total=False)

# === TypeAlias ===
Vector = list[float]
Matrix = list[Vector]
UserId = int
JSON = dict[str, Union[str, int, float, bool, None, list, dict]]

def dot_product(v1: Vector, v2: Vector) -> float:
    return sum(a * b for a, b in zip(v1, v2))

def get_user(user_id: UserId) -> Optional[dict]:
    pass

# === Overload β€” beberapa signature untuk fungsi yang sama ===
from typing import overload

@overload
def process(value: int) -> int: ...
@overload
def process(value: str) -> str: ...
@overload
def process(value: list[int]) -> list[int]: ...

def process(value):
    if isinstance(value, int):
        return value * 2
    elif isinstance(value, str):
        return value.upper()
    elif isinstance(value, list):
        return sorted(value)

5. Generics

Generics memungkinkan Anda menulis kode yang bekerja dengan berbagai tipe data sambil tetap mempertahankan type safety. Ini sangat berguna untuk data structures dan utility functions.

Python β€” Generics
from typing import TypeVar, Generic

# TypeVar β€” tipe placeholder
T = TypeVar('T')         # Tipe apapun
K = TypeVar('K')         # Untuk key
V = TypeVar('V')         # Untuk value
N = TypeVar('N', int, float)  # Hanya int atau float (constrained)

# === Generic Function ===
def first_element(items: list[T]) -> T | None:
    """Mengembalikan elemen pertama dari list"""
    if items:
        return items[0]
    return None

# Type checker tahu hasilnya int karena input list[int]
angka = first_element([1, 2, 3])  # int | None
teks = first_element(["a", "b"])  # str | None

# === Generic Class ===
class Stack(Generic[T]):
    """Stack generik yang bisa menyimpan tipe apapun"""

    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        if not self._items:
            raise IndexError("Stack kosong!")
        return self._items.pop()

    def peek(self) -> T | None:
        if self._items:
            return self._items[-1]
        return None

    def is_empty(self) -> bool:
        return len(self._items) == 0

    def size(self) -> int:
        return len(self._items)

    def __repr__(self) -> str:
        return f"Stack({self._items})"

# Stack dengan tipe spesifik
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
int_stack.push(3)
print(int_stack)       # Stack([1, 2, 3])
print(int_stack.pop()) # 3

str_stack: Stack[str] = Stack()
str_stack.push("hello")
str_stack.push("world")

# === Generic dengan multiple type parameters ===
class Pair(Generic[K, V]):
    """Pasangan key-value generik"""

    def __init__(self, key: K, value: V) -> None:
        self.key = key
        self.value = value

    def __repr__(self) -> str:
        return f"Pair({self.key!r}, {self.value!r})"

    def swap(self) -> 'Pair[V, K]':
        return Pair(self.value, self.key)

p = Pair("nama", 25)
print(p)           # Pair('nama', 25)
print(p.swap())    # Pair(25, 'nama')

# === Generic dengan TypeVar constraints ===
def double(x: N) -> N:
    """Hanya menerima int atau float"""
    return x * 2  # type: ignore

print(double(5))    # 10
print(double(3.14)) # 6.28
# double("hi")  ← mypy error!

6. Protocols (Structural Subtyping)

Protocols memungkinkan Anda mendefinisikan interface berdasarkan struktur (method dan attribute yang dimiliki), bukan berdasarkan inheritance. Ini dikenal sebagai structural subtyping atau duck typing yang ter-typed.

Python β€” Protocols
from typing import Protocol, runtime_checkable

# === Basic Protocol ===
class Drawable(Protocol):
    """Protocol untuk objek yang bisa digambar"""
    def draw(self) -> str: ...

class Printable(Protocol):
    """Protocol untuk objek yang bisa dicetak"""
    def print(self) -> None: ...

# Class yang memenuhi protocol TANPA perlu inherit
class Circle:
    def __init__(self, radius: float) -> None:
        self.radius = radius

    def draw(self) -> str:
        return f"Drawing circle with radius {self.radius}"

class Square:
    def __init__(self, side: float) -> None:
        self.side = side

    def draw(self) -> str:
        return f"Drawing square with side {self.side}"

# Fungsi yang menerima apapun yang punya method draw()
def render(obj: Drawable) -> None:
    print(obj.draw())

render(Circle(5))   # βœ… OK: Circle punya draw()
render(Square(3))   # βœ… OK: Square punya draw()
# render("hello")   # ❌ mypy error: str tidak punya draw()

# === Protocol dengan attributes ===
class HasName(Protocol):
    name: str
    age: int

class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

class Animal:
    def __init__(self, name: str, age: int, species: str) -> None:
        self.name = name
        self.age = age
        self.species = species

def greet(entity: HasName) -> str:
    return f"Hello, {entity.name}! You are {entity.age} years old."

print(greet(Person("Budi", 25)))      # βœ… OK
print(greet(Animal("Kucing", 3, "Persia")))  # βœ… OK

# === runtime_checkable Protocol ===
@runtime_checkable
class Closeable(Protocol):
    def close(self) -> None: ...

# Bisa di-check pada runtime!
import io
f = io.StringIO("hello")
print(isinstance(f, Closeable))  # True (StringIO punya close())

# === Protocol dengan method signatures ===
class Serializer(Protocol):
    def serialize(self, data: dict) -> str: ...
    def deserialize(self, text: str) -> dict: ...

class JSONSerializer:
    def serialize(self, data: dict) -> str:
        import json
        return json.dumps(data)

    def deserialize(self, text: str) -> dict:
        import json
        return json.loads(text)

class CSVSerializer:
    def serialize(self, data: dict) -> str:
        return ",".join(f"{k}={v}" for k, v in data.items())

    def deserialize(self, text: str) -> dict:
        return dict(item.split("=") for item in text.split(","))

def save_data(serializer: Serializer, data: dict) -> str:
    return serializer.serialize(data)

print(save_data(JSONSerializer(), {"a": 1, "b": 2}))  # {"a": 1, "b": 2}
print(save_data(CSVSerializer(), {"a": 1, "b": 2}))    # a=1,b=2

7. Static Type Checking dengan mypy

mypy adalah static type checker untuk Python. Ia memeriksa type hints dalam kode Anda dan mendeteksi kesalahan tipe tanpa menjalankan program. mypy adalah tool yang paling populer dan menjadi standar untuk type checking di Python.

Bash β€” Instalasi mypy
# Instal mypy
pip install mypy

# Jalankan type checking pada file
mypy myfile.py

# Jalankan pada seluruh project
mypy src/

# Dengan strict mode (lebih ketat)
mypy --strict myfile.py

# Contoh konfigurasi di pyproject.toml
# [tool.mypy]
# python_version = "3.12"
# warn_return_any = true
# warn_unused_configs = true
# disallow_untyped_defs = true
# check_untyped_defs = true
Python β€” mypy Examples
# === Contoh error yang bisa dideteksi mypy ===

# Error 1: Tipe mismatch
def tambah(a: int, b: int) -> int:
    return a + b

hasil = tambah("hello", "world")  # ❌ mypy: Argument 1 has incompatible type "str"; expected "int"

# Error 2: Return type mismatch
def get_nama() -> str:
    return 42  # ❌ mypy: Incompatible return value type (got "int", expected "str")

# Error 3: Optional handling
from typing import Optional

def cari(id: int) -> Optional[str]:
    if id == 1:
        return "Budi"
    return None

nama = cari(1)
print(nama.upper())  # ❌ mypy: Item "None" of "Optional[str]" has no attribute "upper"

# Cara yang benar:
nama = cari(1)
if nama is not None:
    print(nama.upper())  # βœ… OK: Type narrowed to str

# Atau gunakan assert:
nama = cari(1)
assert nama is not None
print(nama.upper())  # βœ… OK

# Error 4: Missing type annotation
def fungsi_tanpa_tipe(data):  # ❌ mypy (strict): Function is missing a type annotation
    return data * 2

# Fix: tambahkan type hints
def fungsi_dengan_tipe(data: int) -> int:
    return data * 2

# === Mengatasi error mypy ===

# 1. Type ignore (dengan alasan yang jelas)
x = some_function()  # type: ignore[attr-defined]  # noqa

# 2. Cast
from typing import cast
nilai = cast(int, input_data)  # Memberitahu mypy bahwa ini int

# 3. TypeGuard (Python 3.10+)
from typing import TypeGuard

def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in val)

def process(data: list[object]) -> None:
    if is_string_list(data):
        print(", ".join(data))  # mypy tahu ini list[str] di sini

8. Type Hints Modern (Python 3.10+)

Python 3.10+ membawa banyak penyederhanaan sintaks untuk type hints, membuatnya lebih mudah dibaca dan ditulis.

Python β€” Modern Type Hints
# === Python 3.10+ Union dengan | operator ===

# Sebelum 3.10:
from typing import Union, Optional
def proses_lama(data: Union[str, int]) -> Optional[str]:
    pass

# Python 3.10+: jauh lebih singkat!
def proses_baru(data: str | int) -> str | None:
    pass

# Bisa digunakan di mana saja
nilai: int | float = 42
hasil: str | None = cari_sesuatu()

# === match-case dengan type hints (Python 3.10+) ===
def handle(value: str | int | list[int]) -> str:
    match value:
        case str():
            return f"String: {value}"
        case int():
            return f"Int: {value}"
        case list():
            return f"List: {value}"
        case _:
            return "Unknown"

# === Type Hints di assignment (Python 3.6+, lebih baik 3.10+) ===
# Builtin generics (tidak perlu import List, Dict, dll)
angka: list[int] = [1, 2, 3]           # Bukan List[int]
data: dict[str, int] = {"a": 1}        # Bukan Dict[str, int]
koordinat: tuple[int, int] = (1, 2)    # Bukan Tuple[int, int]
himpunan: set[str] = {"a", "b"}        # Bukan Set[str]

# === ParamSpec (Python 3.10+) ===
from typing import ParamSpec, TypeVar

P = ParamSpec('P')
R = TypeVar('R')

def log_decorator(func: Callable[P, R]) -> Callable[P, R]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

# === TypeVarTuple (Python 3.11+) β€” variadic generics ===
from typing import TypeVarTuple

Ts = TypeVarTuple('Ts')

# === dataclass_transform (Python 3.11+) ===
# Memungkinkan custom dataclass-like classes mendapat mypy support

# === override decorator (Python 3.12+) ===
from typing import override

class Base:
    def method(self) -> str:
        return "base"

class Child(Base):
    @override
    def method(self) -> str:  # mypy cek bahwa Base.method ada
        return "child"

9. Studi Kasus Praktis

API Client dengan Type Hints

Python β€” API Client
from typing import TypedDict, Optional, Literal
from dataclasses import dataclass

# === Definisikan tipe data ===

class UserResponse(TypedDict):
    id: int
    name: str
    email: str
    role: Literal["admin", "user", "viewer"]
    active: bool

@dataclass
class ApiResponse:
    status: int
    data: Optional[dict | list]
    message: str

    @property
    def is_success(self) -> bool:
        return 200 <= self.status < 300

@dataclass
class ApiClient:
    base_url: str
    api_key: str

    def get_user(self, user_id: int) -> Optional[UserResponse]:
        """Mengambil data user dari API"""
        # Simulasi API call
        users: dict[int, UserResponse] = {
            1: {"id": 1, "name": "Budi", "email": "budi@mail.com",
                "role": "admin", "active": True},
            2: {"id": 2, "name": "Ani", "email": "ani@mail.com",
                "role": "user", "active": True},
        }
        return users.get(user_id)

    def search_users(
        self,
        query: str,
        role: Optional[Literal["admin", "user"]] = None,
        limit: int = 10
    ) -> list[UserResponse]:
        """Mencari user berdasarkan query"""
        all_users: list[UserResponse] = [
            {"id": 1, "name": "Budi", "email": "budi@mail.com",
             "role": "admin", "active": True},
            {"id": 2, "name": "Ani", "email": "ani@mail.com",
             "role": "user", "active": True},
        ]

        result = [u for u in all_users if query.lower() in u["name"].lower()]

        if role:
            result = [u for u in result if u["role"] == role]

        return result[:limit]

# Penggunaan
client = ApiClient("https://api.example.com", "key123")
user = client.get_user(1)
if user:
    print(f"User: {user['name']} ({user['role']})")

results = client.search_users("bu", role="admin")
for u in results:
    print(f"  - {u['name']} ({u['email']})")

Repository Pattern dengan Generics

Python β€” Repository Pattern
from typing import TypeVar, Generic, Optional, Protocol
from dataclasses import dataclass, field

class Entity(Protocol):
    @property
    def id(self) -> int: ...

T = TypeVar('T', bound=Entity)

@dataclass
class User:
    id: int
    name: str
    email: str

@dataclass
class Product:
    id: int
    name: str
    price: float

class Repository(Generic[T]):
    """Generic repository pattern"""
    def __init__(self) -> None:
        self._items: dict[int, T] = {}
        self._next_id: int = 1

    def add(self, item: T) -> T:
        self._items[item.id] = item
        return item

    def get_by_id(self, item_id: int) -> Optional[T]:
        return self._items.get(item_id)

    def get_all(self) -> list[T]:
        return list(self._items.values())

    def delete(self, item_id: int) -> bool:
        if item_id in self._items:
            del self._items[item_id]
            return True
        return False

    def count(self) -> int:
        return len(self._items)

# Type-safe repositories
user_repo: Repository[User] = Repository()
product_repo: Repository[Product] = Repository()

user_repo.add(User(1, "Budi", "budi@mail.com"))
user_repo.add(User(2, "Ani", "ani@mail.com"))

product_repo.add(Product(1, "Laptop", 15000000))
product_repo.add(Product(2, "Mouse", 150000))

# mypy tahu hasilnya User
user = user_repo.get_by_id(1)
if user:
    print(f"User: {user.name}")  # βœ… autocomplete untuk .name

# mypy tahu hasilnya Product
product = product_repo.get_by_id(1)
if product:
    print(f"Product: {product.price}")  # βœ… autocomplete untuk .price

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Type Hints:

Pertanyaan 1: Apakah type hints memaksa tipe data pada runtime di Python?

a) Ya, Python akan menolak tipe yang salah
b) Tidak, type hints hanya sebagai dokumentasi dan untuk static analysis
c) Hanya untuk integer dan string
d) Hanya pada Python 3.12+

Pertanyaan 2: Apa arti Optional[str] dalam type hints?

a) String yang opsional (bisa diabaikan)
b) Union[str, None] β€” string atau None
c) String kosong
d) String dengan default value

Pertanyaan 3: Apa fungsi dari TypeVar dalam modul typing?

a) Membuat variabel baru
b) Mengubah tipe data pada runtime
c) Placeholder untuk tipe dalam generics
d) Deklarasi konstanta

Pertanyaan 4: Apa keunggulan Protocol dibanding ABC (Abstract Base Class)?

a) Protocol lebih cepat
b) Protocol tidak perlu inheritance β€” cukup punya method/attribute yang sesuai
c) Protocol bisa digunakan di Python 2
d) Tidak ada perbedaan

Pertanyaan 5: Di Python 3.10+, cara menulis Union type yang benar adalah?

a) Union[str, int]
b) str | int
c) str + int
d) (str, int)
πŸ” Zoom
100%
🎨 Tema