Konstanta #

Python tidak memiliki keyword khusus untuk mendefinisikan konstanta seperti const di JavaScript atau final di Java. Tidak ada mekanisme bawaan yang mencegah sebuah nilai diubah setelah ditetapkan. Yang Python miliki adalah konvensi: nama yang ditulis seluruhnya dengan huruf kapital diperlakukan sebagai sinyal kepada developer lain bahwa nilai ini tidak boleh diubah. Konvensi ini terdengar lemah, tapi dalam praktiknya sangat efektif karena kode Python bergantung besar pada kesepakatan komunitas. Artikel ini membahas cara menggunakan konstanta dengan benar di Python: dari konvensi dasar, cara mengamankannya dengan Final dan Enum, hingga pola pengorganisasian konstanta di proyek yang lebih besar.

Konvensi UPPER_SNAKE_CASE #

Cara paling umum mendefinisikan konstanta di Python adalah menggunakan nama dengan seluruh huruf kapital dan kata-kata dipisahkan oleh underscore. Ini adalah konvensi PEP 8 yang dipahami semua developer Python.

# Konstanta nilai matematis dan fisika
PI = 3.141592653589793
GRAVITASI = 9.80665          # m/s²
KECEPATAN_CAHAYA = 299_792_458  # m/s

# Konstanta konfigurasi aplikasi
MAX_KONEKSI = 100
TIMEOUT_DETIK = 30
UKURAN_HALAMAN = 20

# Konstanta string
BASE_URL = "https://api.example.com/v1"
VERSI_API = "v1.2.0"
NAMA_APLIKASI = "SistemKasir"

# Konstanta path
DIR_UPLOAD = "/var/uploads"
DIR_LOG = "/var/log/app"

Angka besar bisa ditulis dengan underscore sebagai pemisah ribuan agar lebih mudah dibaca:

# ANTI-PATTERN: angka besar tanpa pemisah — sulit dibaca
BATAS_KREDIT = 50000000
POPULASI_INDONESIA = 270000000

# BENAR: gunakan underscore sebagai pemisah ribuan
BATAS_KREDIT = 50_000_000
POPULASI_INDONESIA = 270_000_000

Mengapa Konstanta Penting: Magic Number #

Masalah terbesar yang dipecahkan oleh konstanta adalah magic number — angka atau string literal yang muncul begitu saja dalam kode tanpa penjelasan maknanya.

# ANTI-PATTERN: magic number — apa arti 0.11, 18, dan 3?
def hitung_total(harga, umur):
    pajak = harga * 0.11
    if umur < 18:
        return -1
    if len(keranjang) > 3:
        terapkan_diskon()
    return harga + pajak

# Pertanyaan yang tidak terjawab dari kode di atas:
# - 0.11 adalah PPN? tarif apa? kapan berubah?
# - 18 adalah batas usia legal? untuk apa?
# - 3 adalah batas minimum item? maksimum?
# - -1 menandakan apa? error? tidak valid?
# BENAR: ganti magic number dengan konstanta bernama
TARIF_PPN = 0.11          # PPN 11% berlaku sejak April 2022
BATAS_USIA_DEWASA = 18    # sesuai UU Perlindungan Anak
MIN_ITEM_DISKON = 3       # diskon berlaku mulai pembelian ke-4
KODE_BUKAN_DEWASA = -1    # kode return jika pembeli di bawah umur

def hitung_total(harga, umur):
    pajak = harga * TARIF_PPN
    if umur < BATAS_USIA_DEWASA:
        return KODE_BUKAN_DEWASA
    if len(keranjang) > MIN_ITEM_DISKON:
        terapkan_diskon()
    return harga + pajak

Manfaat langsung: jika tarif PPN berubah, kamu hanya perlu mengubah satu baris (TARIF_PPN = 0.12), bukan mencari semua kemunculan angka 0.11 di seluruh codebase.


Final — Anotasi Konstanta yang Tidak Boleh Di-reassign #

Sejak Python 3.8, modul typing menyediakan Final yang bisa digunakan sebagai anotasi tipe sekaligus sinyal kepada linter bahwa variabel ini tidak boleh di-reassign. Python tetap tidak mencegah perubahan saat runtime, tapi tool seperti mypy akan menandai pelanggaran sebagai error.

from typing import Final

# Deklarasi konstanta dengan Final
PI: Final = 3.141592653589793
MAX_RETRY: Final[int] = 3
BASE_URL: Final[str] = "https://api.example.com"

# mypy akan menandai ini sebagai error:
PI = 3.14          # error: Cannot assign to final name "PI"
MAX_RETRY = 5      # error: Cannot assign to final name "MAX_RETRY"

Final juga bisa digunakan di dalam kelas untuk atribut yang tidak boleh di-override oleh subkelas:

from typing import Final

class KonfigurasiDatabase:
    HOST: Final = "localhost"
    PORT: Final[int] = 5432
    NAMA_DB: Final = "produksi"

    def __init__(self):
        self.username = "admin"    # atribut biasa, bisa berubah
        self.password = "secret"   # atribut biasa, bisa berubah
Final bekerja sebagai anotasi statis — hanya dikenali oleh linter dan IDE, bukan oleh Python saat runtime. Untuk benar-benar mencegah perubahan nilai saat runtime, gunakan Enum atau property dengan getter saja (tanpa setter).

Enum — Konstanta Berkelompok #

Enum (Enumeration) adalah cara terbaik untuk mengelompokkan konstanta yang saling terkait. Alih-alih mendefinisikan banyak konstanta terpisah, kamu bungkus semuanya dalam satu kelas Enum.

Enum Dasar #

from enum import Enum

class StatusPesanan(Enum):
    MENUNGGU = "pending"
    DIPROSES = "processing"
    DIKIRIM = "shipped"
    SELESAI = "completed"
    DIBATALKAN = "cancelled"

# Penggunaan
pesanan_status = StatusPesanan.MENUNGGU
print(pesanan_status)        # → StatusPesanan.MENUNGGU
print(pesanan_status.value)  # → pending
print(pesanan_status.name)   # → MENUNGGU

# Perbandingan — selalu gunakan member Enum, bukan string langsung
if pesanan_status == StatusPesanan.MENUNGGU:
    print("Pesanan belum diproses")

# Iterasi semua member
for status in StatusPesanan:
    print(status.name, "→", status.value)

IntEnum untuk Konstanta Integer #

from enum import IntEnum

class PrioritasTiket(IntEnum):
    RENDAH = 1
    SEDANG = 2
    TINGGI = 3
    KRITIS = 4

# IntEnum bisa dibandingkan langsung dengan integer
tiket_prio = PrioritasTiket.TINGGI
print(tiket_prio > 2)        # → True
print(tiket_prio == 3)       # → True
print(int(tiket_prio))       # → 3

# Berguna untuk pengurutan
tiket_list = [
    PrioritasTiket.RENDAH,
    PrioritasTiket.KRITIS,
    PrioritasTiket.SEDANG,
]
print(sorted(tiket_list))
# → [<PrioritasTiket.RENDAH: 1>, <PrioritasTiket.SEDANG: 2>, <PrioritasTiket.KRITIS: 4>]

Mengapa Enum Lebih Baik dari String Konstanta Biasa #

# ANTI-PATTERN: konstanta string terpisah — mudah salah ketik, tidak ada autocomplete
STATUS_MENUNGGU = "pending"
STATUS_DIPROSES = "processing"
STATUS_SELESAI = "completed"

def update_status(pesanan_id, status):
    if status == "pendng":      # ← typo! tidak akan error, tapi logika salah
        kirim_notif_menunggu()

# BENAR: Enum mencegah nilai tidak valid
from enum import Enum

class StatusPesanan(Enum):
    MENUNGGU = "pending"
    DIPROSES = "processing"
    SELESAI = "completed"

def update_status(pesanan_id, status: StatusPesanan):
    if status == StatusPesanan.MENUNGGU:
        kirim_notif_menunggu()

# Tidak bisa salah ketik — IDE menyediakan autocomplete
update_status(1, StatusPesanan.MENUNGGU)   # ✓
update_status(1, "pendng")                 # ✗ mypy akan menandai ini

auto() untuk Nilai Otomatis #

from enum import Enum, auto

class Hari(Enum):
    SENIN = auto()      # → 1
    SELASA = auto()     # → 2
    RABU = auto()       # → 3
    KAMIS = auto()      # → 4
    JUMAT = auto()      # → 5
    SABTU = auto()      # → 6
    MINGGU = auto()     # → 7

print(Hari.SENIN.value)   # → 1
print(Hari.JUMAT.value)   # → 5

Modul Konstanta Terpusat #

Untuk proyek yang lebih besar, pisahkan semua konstanta ke dalam satu modul (atau beberapa modul berdasarkan kategori). Ini membuat perubahan konfigurasi lebih mudah dilacak dan tidak tersebar di seluruh codebase.

Struktur yang Direkomendasikan #

proyek/
  ├── constants/
  │   ├── __init__.py
  │   ├── api.py        ← konstanta terkait API
  │   ├── database.py   ← konstanta terkait database
  │   └── bisnis.py     ← aturan dan logika bisnis
  ├── main.py
  └── utils.py

constants/api.py #

# constants/api.py
from typing import Final

BASE_URL: Final = "https://api.example.com/v1"
TIMEOUT_DETIK: Final[int] = 30
MAX_RETRY: Final[int] = 3
UKURAN_HALAMAN: Final[int] = 20

HEADER_DEFAULT: Final = {
    "Content-Type": "application/json",
    "Accept": "application/json",
}

constants/bisnis.py #

# constants/bisnis.py
from typing import Final
from enum import Enum

# Batas dan limit
MIN_SALDO: Final[int] = 10_000
MAX_TRANSFER_HARIAN: Final[int] = 25_000_000
BATAS_KREDIT_DEFAULT: Final[int] = 5_000_000

# Tarif
TARIF_PPN: Final[float] = 0.11
TARIF_BUNGA_BULANAN: Final[float] = 0.015
BIAYA_TRANSFER: Final[int] = 6_500

# Status transaksi
class StatusTransaksi(Enum):
    PENDING = "pending"
    SUKSES = "success"
    GAGAL = "failed"
    DIBATALKAN = "cancelled"
    KEDALUWARSA = "expired"

Cara Import #

# main.py
from constants.api import BASE_URL, TIMEOUT_DETIK, MAX_RETRY
from constants.bisnis import TARIF_PPN, StatusTransaksi, MIN_SALDO

def proses_pembayaran(jumlah: int) -> StatusTransaksi:
    if jumlah < MIN_SALDO:
        return StatusTransaksi.GAGAL

    pajak = jumlah * TARIF_PPN
    total = jumlah + pajak

    # ... proses pembayaran ...
    return StatusTransaksi.SUKSES

Konstanta yang Terlindungi dengan property #

Jika kamu benar-benar ingin konstanta yang tidak bisa diubah saat runtime (bukan hanya konvensi), gunakan property pada kelas tanpa setter:

class KonfigurasiApp:
    """Konfigurasi aplikasi yang tidak bisa diubah setelah inisialisasi."""

    def __init__(self):
        self._versi = "2.1.0"
        self._nama = "SistemKasir"
        self._mode_debug = False

    @property
    def versi(self) -> str:
        return self._versi

    @property
    def nama(self) -> str:
        return self._nama

    @property
    def mode_debug(self) -> bool:
        return self._mode_debug

# Penggunaan
config = KonfigurasiApp()
print(config.versi)       # → 2.1.0
print(config.nama)        # → SistemKasir

config.versi = "3.0.0"   # AttributeError: can't set attribute

Pendekatan ini cocok untuk konfigurasi yang nilainya ditentukan saat startup (misalnya dibaca dari environment variable) dan tidak boleh berubah selama aplikasi berjalan.


Konstanta Bawaan Python #

Python sendiri mendefinisikan beberapa konstanta bawaan yang penting untuk diketahui:

# Nilai khusus bawaan Python
print(True)     # → True  (bool)
print(False)    # → False (bool)
print(None)     # → None  (NoneType) — merepresentasikan "tidak ada nilai"

# Konstanta matematika dari modul math
import math
print(math.pi)   # → 3.141592653589793
print(math.e)    # → 2.718281828459045
print(math.inf)  # → inf  (tak hingga)
print(math.nan)  # → nan  (Not a Number)

# Cek nilai khusus
print(math.isinf(math.inf))  # → True
print(math.isnan(math.nan))  # → True

# Konstanta sistem dari modul sys
import sys
print(sys.maxsize)    # → 9223372036854775807 (integer maksimum di platform ini)
print(sys.version)    # → versi Python yang sedang berjalan

Ringkasan #

  • Konvensi UPPER_SNAKE_CASE adalah cara standar mendefinisikan konstanta di Python — dipahami semua developer Python dan tidak memerlukan import tambahan.
  • Hilangkan magic number — setiap angka atau string literal yang maknanya tidak jelas dari konteksnya harus diganti dengan konstanta bernama. Ini membuat kode lebih mudah dibaca sekaligus lebih mudah diubah.
  • Gunakan Final dari typing untuk memberi sinyal kepada linter bahwa variabel tidak boleh di-reassign — mypy akan menandai pelanggaran sebagai error.
  • Gunakan Enum untuk konstanta yang saling terkait dalam satu kelompok — mencegah typo, mengaktifkan autocomplete IDE, dan membuat kode lebih ekspresif.
  • IntEnum cocok jika konstanta perlu dibandingkan atau diurutkan secara numerik.
  • Pisahkan konstanta ke modul tersendiri (constants/) pada proyek yang lebih besar — perubahan nilai hanya perlu dilakukan di satu tempat.
  • property tanpa setter adalah cara satu-satunya untuk benar-benar mencegah perubahan nilai saat runtime — cocok untuk konfigurasi yang dibaca saat startup.
  • Gunakan underscore sebagai pemisah ribuan pada konstanta numerik besar (50_000_000) agar lebih mudah dibaca.

← Sebelumnya: Variabel   Berikutnya: Tipe Data →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact