Variabel #

Variabel adalah nama yang menunjuk ke sebuah nilai di memori. Di Python, konsep ini lebih tepat disebut sebagai name binding — kamu tidak membuat “kotak” berisi nilai, melainkan memberi nama pada objek yang sudah ada. Perbedaan ini terasa abstrak di awal, tapi menjadi sangat penting saat kamu bekerja dengan objek yang bisa dimodifikasi (mutable), atau saat dua variabel menunjuk ke objek yang sama. Artikel ini membahas cara kerja variabel Python dari dasar: assignment, penamaan, unpacking, scope, hingga type hints yang membuat kode lebih eksplisit dan aman.

Assignment dan Dynamic Typing #

Di Python, variabel dibuat saat pertama kali kamu menetapkan nilai padanya menggunakan operator =. Tidak ada deklarasi tipe yang diperlukan — Python secara otomatis menentukan tipe berdasarkan nilai yang ditetapkan.

# Assignment dasar
nama = "Budi"        # str
umur = 25            # int
tinggi = 175.5       # float
aktif = True         # bool

# Cek tipe dengan type()
print(type(nama))    # → <class 'str'>
print(type(umur))    # → <class 'int'>
print(type(tinggi))  # → <class 'float'>
print(type(aktif))   # → <class 'bool'>

Python adalah bahasa dynamically typed — sebuah variabel bisa menunjuk ke nilai bertipe berbeda dalam satu program yang sama:

data = 42           # data adalah int
print(type(data))   # → <class 'int'>

data = "empat dua" # sekarang data adalah str
print(type(data))   # → <class 'str'>

data = [4, 2]       # sekarang data adalah list
print(type(data))   # → <class 'list'>

Ini berbeda dengan bahasa statically typed seperti Java atau Go, di mana tipe variabel ditentukan saat deklarasi dan tidak bisa berubah.

Cara Python Menyimpan Variabel #

Memahami cara Python mengelola memori mencegah banyak bug yang membingungkan:

# Setiap assignment membuat "label" yang menunjuk ke objek
a = [1, 2, 3]
b = a             # b dan a menunjuk ke OBJEK YANG SAMA

b.append(4)
print(a)          # → [1, 2, 3, 4]  ← a ikut berubah!
print(b)          # → [1, 2, 3, 4]

# Verifikasi bahwa keduanya menunjuk ke objek yang sama
print(a is b)     # → True
print(id(a) == id(b))  # → True (id() mengembalikan alamat memori)
# ANTI-PATTERN: mengira assignment membuat salinan otomatis
def tambah_item(keranjang):
    keranjang.append("item baru")  # memodifikasi objek asli!

belanjaan = ["apel", "jeruk"]
tambah_item(belanjaan)
print(belanjaan)  # → ["apel", "jeruk", "item baru"]  ← tidak terduga?

# BENAR: buat salinan eksplisit jika tidak ingin memodifikasi aslinya
def tambah_item_aman(keranjang):
    salinan = keranjang.copy()     # atau: list(keranjang)
    salinan.append("item baru")
    return salinan

belanjaan = ["apel", "jeruk"]
hasil = tambah_item_aman(belanjaan)
print(belanjaan)  # → ["apel", "jeruk"]  ← tidak berubah
print(hasil)      # → ["apel", "jeruk", "item baru"]
Perilaku ini hanya berlaku untuk objek mutable (list, dict, set). Untuk objek immutable (int, str, tuple, float), assignment selalu membuat binding baru — tidak ada efek samping tersembunyi.

Aturan Penamaan Variabel #

Python memiliki aturan keras (akan error jika dilanggar) dan konvensi lunak (tidak error, tapi tidak idiomatis):

Aturan Keras #

# ✓ Boleh: huruf, angka (bukan di awal), underscore
nama_pengguna = "alice"
data2024 = []
_private = True
__sangat_private = False

# ✗ Error: dimulai dengan angka
# 2024data = []       # SyntaxError

# ✗ Error: mengandung karakter khusus
# nama-pengguna = ""  # SyntaxError (dikira operasi pengurangan)
# email@domain = ""   # SyntaxError
# nilai% = 0          # SyntaxError

# ✗ Error: menggunakan keyword Python
# if = 10             # SyntaxError
# for = []            # SyntaxError
# class = "A"         # SyntaxError

Konvensi Penamaan (PEP 8) #

# ✓ snake_case untuk variabel dan fungsi
jumlah_mahasiswa = 30
nama_lengkap = "John Doe"
is_aktif = True

# ✗ camelCase (tidak idiomatis di Python, meski tidak error)
jumlahMahasiswa = 30    # gaya Java/JavaScript
NamaLengkap = "John"   # gaya C#

# ✓ UPPER_SNAKE_CASE untuk konstanta
BATAS_KREDIT = 50_000_000
TARIF_PAJAK = 0.11

# ✓ PascalCase untuk nama kelas
class DataPengguna:
    pass

# ✓ Prefix underscore untuk konvensi privat
_cache = {}             # protected (satu underscore)
__internal_state = {}   # name-mangled (dua underscore)

# ✓ Underscore tunggal untuk variabel yang tidak dipakai
for _ in range(5):
    print("halo")

Nama yang Harus Dihindari #

# ANTI-PATTERN: menimpa built-in Python — tidak error tapi sangat berbahaya
list = [1, 2, 3]       # menimpa built-in list()
dict = {"a": 1}        # menimpa built-in dict()
str = "hello"          # menimpa built-in str()
id = 42                # menimpa built-in id()
type = "integer"       # menimpa built-in type()
input = "data"         # menimpa built-in input()
len = 10               # menimpa built-in len()

# Setelah baris di atas, kamu tidak bisa lagi menggunakan:
angka = list(range(5)) # TypeError: 'list' object is not callable

# BENAR: gunakan nama yang lebih deskriptif
angka_list = [1, 2, 3]
konfigurasi = {"a": 1}
pesan_error = "hello"
user_id = 42

Multiple Assignment #

Python menyediakan beberapa cara ringkas untuk melakukan assignment ke banyak variabel sekaligus.

# Assignment berantai — semua variabel menunjuk ke OBJEK YANG SAMA
a = b = c = 0
print(a, b, c)   # → 0 0 0

# Aman untuk immutable (int, str, float)
a = b = c = 0
a = 10           # membuat binding baru untuk a saja
print(a, b, c)   # → 10 0 0  ← b dan c tidak berubah

# BERBAHAYA untuk mutable (list, dict)
x = y = z = []   # ketiganya menunjuk ke LIST YANG SAMA
x.append(1)
print(y)         # → [1]  ← y ikut berubah!

# BENAR untuk mutable: buat objek terpisah
x = []
y = []
z = []
# Tuple unpacking — assignment beberapa variabel sekaligus
a, b, c = 1, 2, 3
print(a, b, c)   # → 1 2 3

# Swap nilai tanpa variabel sementara — idiom khas Python
x = 10
y = 20
x, y = y, x
print(x, y)      # → 20 10

# ANTI-PATTERN: swap gaya bahasa lain
# temp = x
# x = y
# y = temp

Variable Unpacking #

Unpacking adalah cara mengekstrak elemen dari koleksi data langsung ke variabel individual. Ini adalah salah satu fitur Python yang paling ekspresif.

Unpacking Dasar #

# Unpacking dari tuple
koordinat = (10.5, -6.2)
latitude, longitude = koordinat
print(latitude)   # → 10.5
print(longitude)  # → -6.2

# Unpacking dari list
rgb = [255, 128, 0]
merah, hijau, biru = rgb
print(f"R={merah}, G={hijau}, B={biru}")  # → R=255, G=128, B=0

# Unpacking dari string
huruf_a, huruf_b, huruf_c = "ABC"
print(huruf_a)  # → A

Extended Unpacking dengan * #

Operator * (star) menangkap sisa elemen yang tidak di-unpack secara eksplisit:

# Ambil elemen pertama dan terakhir, sisanya ditampung di tengah
pertama, *tengah, terakhir = [1, 2, 3, 4, 5]
print(pertama)   # → 1
print(tengah)    # → [2, 3, 4]
print(terakhir)  # → 5

# Ambil hanya beberapa dari banyak nilai
kepala, *ekor = [10, 20, 30, 40, 50]
print(kepala)    # → 10
print(ekor)      # → [20, 30, 40, 50]

*awal, buntut = [10, 20, 30, 40, 50]
print(awal)      # → [10, 20, 30, 40]
print(buntut)    # → 50

Unpacking di Loop #

# Unpacking langsung di for loop — sangat umum digunakan
pasangan = [(1, "satu"), (2, "dua"), (3, "tiga")]

# ANTI-PATTERN: akses via indeks
for item in pasangan:
    print(item[0], item[1])

# BENAR: unpack langsung di header for
for angka, kata in pasangan:
    print(angka, kata)

# Contoh nyata dengan enumerate()
buah = ["apel", "jeruk", "mangga"]
for indeks, nama in enumerate(buah):
    print(f"{indeks}: {nama}")
# → 0: apel
# → 1: jeruk
# → 2: mangga

# Contoh nyata dengan dict.items()
harga = {"apel": 5000, "jeruk": 8000, "mangga": 12000}
for nama_buah, harga_satuan in harga.items():
    print(f"{nama_buah}: Rp{harga_satuan:,}")

Unpacking untuk Mengabaikan Nilai #

Gunakan _ untuk nilai yang tidak kamu butuhkan:

# Abaikan elemen tengah
pertama, _, terakhir = (10, 99, 20)
print(pertama, terakhir)  # → 10 20

# Abaikan banyak elemen
nama, _, _, kota = ("Budi", "25", "Laki-laki", "Jakarta")
print(nama, kota)  # → Budi Jakarta

# Abaikan semua sisa
kepala, *_ = [1, 2, 3, 4, 5]
print(kepala)  # → 1

Scope Variabel #

Scope menentukan di mana sebuah variabel bisa diakses. Python menggunakan aturan LEGB: Local → Enclosing → Global → Built-in.

Urutan pencarian variabel Python (LEGB):

Built-in  (len, print, type, ...)
    │
    ▼
Global    (variabel di level modul/file)
    │
    ▼
Enclosing (variabel di fungsi luar, untuk nested function)
    │
    ▼
Local     (variabel di dalam fungsi saat ini)

Local Scope #

Variabel yang dibuat di dalam fungsi hanya bisa diakses di dalam fungsi itu:

def hitung():
    hasil = 100     # variabel lokal
    print(hasil)    # → 100

hitung()
print(hasil)        # NameError: name 'hasil' is not defined

Global Scope #

Variabel di luar fungsi bisa dibaca dari dalam fungsi, tapi tidak bisa dimodifikasi tanpa keyword global:

pesan = "Halo"   # variabel global

def tampilkan():
    print(pesan)  # ✓ bisa dibaca

def ubah():
    pesan = "Bye" # ← ini membuat variabel LOKAL baru, bukan mengubah global
    print(pesan)  # → Bye

tampilkan()  # → Halo
ubah()       # → Bye
print(pesan) # → Halo  ← global tidak berubah

# Untuk benar-benar mengubah global:
def ubah_global():
    global pesan
    pesan = "Bye"

ubah_global()
print(pesan)  # → Bye
Penggunaan global sebaiknya dihindari kecuali benar-benar diperlukan. Variabel global yang dimodifikasi dari dalam fungsi membuat alur data sulit dilacak dan kode sulit di-test. Solusi yang lebih baik: kembalikan nilai dari fungsi dan tangkap hasilnya di luar.

Nonlocal Scope #

nonlocal digunakan di dalam fungsi bersarang (nested function) untuk mengakses variabel dari fungsi yang melingkupinya:

def buat_counter():
    hitung = 0                  # variabel di enclosing scope

    def tambah():
        nonlocal hitung         # referensi ke hitung di buat_counter
        hitung += 1
        return hitung

    return tambah

counter = buat_counter()
print(counter())  # → 1
print(counter())  # → 2
print(counter())  # → 3

Type Hints (Anotasi Tipe) #

Sejak Python 3.5, kamu bisa menambahkan anotasi tipe pada variabel dan parameter fungsi. Ini tidak mengubah perilaku program (Python tetap dinamis), tapi membantu IDE, linter, dan developer lain memahami kode lebih cepat.

# Anotasi variabel
nama: str = "Budi"
umur: int = 25
tinggi: float = 175.5
aktif: bool = True

# Anotasi tanpa nilai awal (hanya deklarasi tipe)
class Pengguna:
    nama: str
    email: str
    umur: int
from typing import Optional, Union, List, Dict

# Optional — variabel boleh None
def cari_pengguna(user_id: int) -> Optional[str]:
    # mengembalikan nama atau None jika tidak ditemukan
    ...

# Union — bisa salah satu dari beberapa tipe (Python 3.9 ke bawah)
def format_nilai(nilai: Union[int, float]) -> str:
    return f"{nilai:.2f}"

# Sintaks baru dengan | (Python 3.10+)
def format_nilai(nilai: int | float) -> str:
    return f"{nilai:.2f}"

# Koleksi dengan tipe elemen
def hitung_rata(data: List[float]) -> float:
    return sum(data) / len(data)

def cari_harga(katalog: Dict[str, int], nama: str) -> int:
    return katalog.get(nama, 0)
# ANTI-PATTERN: anotasi yang tidak konsisten dengan nilai aktual
# (tidak error, tapi menyesatkan dan akan ditandai linter)
umur: int = "dua puluh lima"    # tipe str, bukan int
aktif: bool = 1                  # int, bukan bool

# BENAR: anotasi sesuai nilai aktual
umur: int = 25
aktif: bool = True
Type hints bersifat opsional dan tidak di-enforce oleh Python saat runtime. Untuk benar-benar memeriksa konsistensi tipe, gunakan tool seperti mypy (pip install mypy) atau aktifkan type checking di VS Code lewat Pylance.

Menghapus Variabel #

Keyword del menghapus binding antara nama variabel dan objeknya. Setelah del, variabel tidak lagi bisa diakses.

x = 100
print(x)   # → 100

del x
print(x)   # → NameError: name 'x' is not defined

# Berguna untuk melepas referensi ke objek besar
import numpy as np
data_besar = np.zeros((10000, 10000))   # ~800MB
# ... proses data ...
del data_besar   # membantu garbage collector membebaskan memori lebih cepat

Ringkasan #

  • Variabel Python adalah label, bukan kotak — assignment mengikat nama ke objek, bukan menyalin nilai. Dua variabel bisa menunjuk ke objek mutable yang sama dan saling mempengaruhi.
  • Dynamic typing — tipe ditentukan dari nilai, bukan dari deklarasi. Variabel bisa “berganti tipe” saat runtime, tapi ini jarang menjadi praktik yang baik.
  • Jangan timpa built-in — hindari nama seperti list, dict, str, id, type, input sebagai nama variabel.
  • a = b = c = 0 aman untuk immutable, tapi berbahaya untuk mutable — gunakan assignment terpisah untuk list/dict.
  • Unpacking membuat kode lebih ekspresif — gunakan a, b = b, a untuk swap, for k, v in d.items() untuk iterasi dict, dan *sisa untuk menangkap elemen yang tersisa.
  • Scope LEGB — Python mencari variabel dari Local → Enclosing → Global → Built-in. Gunakan global dan nonlocal dengan hemat.
  • Hindari global — lebih baik kembalikan nilai dari fungsi daripada memodifikasi variabel global secara langsung.
  • Type hints tidak mengubah perilaku program tapi sangat membantu IDE dan kolaborator memahami kode lebih cepat.

← Sebelumnya: Komentar   Berikutnya: Konstanta →

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