Status pesanan adalah contoh klasik data dengan himpunan nilai tetap: baru, dibayar, dikirim, selesai. Jika nilai-nilai tersebut ditulis sebagai string atau angka lepas di seluruh kode, satu typo — "dibayr" — lolos tanpa galat dan berubah menjadi bug logika yang senyap. Module enum menyediakan class Enum untuk mengelompokkan konstanta bernama dalam satu tempat, menjadikannya satu-satunya sumber kebenaran bagi himpunan nilai tersebut.
Enumerasi Dasar dengan class Enum #
Sebuah Enum didefinisikan sebagai class yang mewarisi Enum; setiap anggota class body menjadi member enumerasi. Setiap member adalah objek tunggal (singleton): mengakses StatusPesanan.DIBAYAR selalu mengembalikan objek yang sama persis.
>>> from enum import Enum
>>> class StatusPesanan(Enum):
... BARU = "baru"
... DIBAYAR = "dibayar"
... DIKIRIM = "dikirim"
... SELESAI = "selesai"
...
>>> StatusPesanan.DIBAYAR
<StatusPesanan.DIBAYAR: 'dibayar'>
>>> StatusPesanan.DIBAYAR.name
'DIBAYAR'
>>> StatusPesanan.DIBAYAR.value
'dibayar'Setiap member memiliki dua sisi: atribut name (identifier dalam class body) dan atribut value (nilai yang kita tetapkan). Keduanya berbeda dari objek member itu sendiri, dan representasi <StatusPesanan.DIBAYAR: 'dibayar'> menampilkan keduanya. Bandingkan dengan konstanta biasa: StatusPesanan.DIBAYAR membawa nama, nilai, dan tipe dalam satu objek, bukan sekadar string telanjang.
Lookup dua arah juga tersedia — member bisa dicari berdasarkan nilai:
from enum import Enum
class StatusPesanan(Enum):
BARU = "baru"
DIBAYAR = "dibayar"
DIKIRIM = "dikirim"
SELESAI = "selesai"
status = StatusPesanan.DIBAYAR
print(status)
print(status.name)
print(status.value)
print(status is StatusPesanan.DIBAYAR)
# pencarian anggota berdasarkan nilai
print(StatusPesanan("selesai"))
# nilai yang tidak terdaftar ditolak sejak awal
try:
StatusPesanan("batal")
except ValueError as e:
print("ValueError:", e)Output yang Dihasilkan:
StatusPesanan.DIBAYAR
DIBAYAR
dibayar
True
StatusPesanan.SELESAI
ValueError: 'batal' is not a valid StatusPesananPerhatikan baris print(StatusPesanan("selesai")): pencarian berdasarkan nilai mengembalikan member StatusPesanan.SELESAI yang utuh. Sebaliknya, nilai "batal" yang tidak terdaftar langsung ditolak dengan ValueError — galat yang muncul di tempat kesalahan terjadi, bukan bug senyap tiga modul kemudian. Karena setiap member adalah singleton, pembanding yang tepat adalah is, sebagaimana dijelaskan pada Chapter Operator Identity.
auto() dan Iterasi Anggota #
Untuk enumerasi berbasis angka, fungsi auto() mengisi nilai berurutan mulai dari 1, dan seluruh member bisa diiterasi langsung dari class-nya:
from enum import Enum, auto
class Level(Enum):
BRONZE = auto()
SILVER = auto()
GOLD = auto()
for level in Level:
print(f"{level.name} = {level.value}")
print(list(Level))
print(Level(3))
# membership test: hanya anggota terdaftar yang lolos
print("Level.GOLD" in Level.__members__)
print(Level.GOLD in Level)Output yang Dihasilkan:
BRONZE = 1
SILVER = 2
GOLD = 3
[<Level.BRONZE: 1>, <Level.SILVER: 2>, <Level.GOLD: 3>]
Level.GOLD
False
TrueIterasi for level in Level menghasilkan member sesuai urutan deklarasi, dan Level(3) mencari member berdasarkan nilai. Ekspresi Level.GOLD in Level menguji keanggotaan dengan objek member — hasil True — sedangkan string "Level.GOLD" ditolak karena bukan member. Dengan konstanta angka lepas, pengujian seperti if level == 3 tidak memiliki perlindungan semacam ini.
IntEnum dan StrEnum #
Enum biasa tidak bisa dibandingkan langsung dengan nilai mentahnya: StatusPesanan.DIBAYAR == "dibayar" bernilai False. Ketika enumerasi memang harus berbaur dengan integer atau string, enum menyediakan varian campuran:
from enum import IntEnum, StrEnum
class Prioritas(IntEnum):
RENDAH = 1
TINGGI = 3
class Warna(StrEnum):
MERAH = "merah"
BIRU = "biru"
# IntEnum dibandingkan langsung dengan integer
print(Prioritas.TINGGI > Prioritas.RENDAH)
print(Prioritas.TINGGI > 2)
print(Prioritas.TINGGI == 3)
# StrEnum berperilaku seperti string
print(Warna.MERAH.upper())
print(f"Warna favorit: {Warna.BIRU}")
print(Warna.BIRU == "biru")Output yang Dihasilkan:
True
True
True
MERAH
Warna favorit: biru
TrueMember IntEnum mengikuti aturan perbandingan integer, dan member StrEnum (hadir sejak Python 3.11) adalah string sungguhan — Warna.BIRU == "biru" bernilai True dan f-string menampilkan "biru", bukan <Warna.BIRU: 'biru'>. Varian ini berguna pada batas sistem lama, misalnya API yang menerima angka atau teks mentah.
Alias, @unique, dan Alasan Meninggalkan Konstanta Biasa #
Nilai duplikat pada Enum tidak menambah member baru, melainkan membuat alias untuk member pertama:
from enum import Enum, unique
# nilai duplikat otomatis menjadi alias anggota pertama
class Bentuk(Enum):
KOTAK = 1
PERSEGI = 1 # alias untuk KOTAK
print(Bentuk.PERSEGI)
print(Bentuk.KOTAK is Bentuk.PERSEGI)
print(list(Bentuk))
# @unique mencegah alias yang tidak disengaja
try:
@unique
class Saluran(Enum):
EMAIL = 1
WHATSAPP = 1
except ValueError as e:
print("ValueError:", e)Output yang Dihasilkan:
Bentuk.KOTAK
True
[<Bentuk.KOTAK: 1>]
ValueError: duplicate values found in <enum 'Saluran'>: WHATSAPP -> EMAILBentuk.PERSEGI hanyalah nama lain untuk Bentuk.KOTAK, dan iterasi hanya menghasilkan member asli. Jika duplikasi tidak disengaja — misalnya salah ketuk nilai — decorator @unique mengubahnya menjadi ValueError yang terlihat segera.