Lingkup dan LEGB

Aturan lingkup LEGB, pernyataan global dan nonlocal, serta konsekuensinya.

Daftar isi

Setiap nama pada Python berlaku di dalam wilayah tertentu yang disebut lingkup (scope). Interpreter mencari nama dengan aturan LEGB, yaitu Local, Enclosing, Global, dan Built-in, sehingga satu nama yang sama dapat menunjuk nilai yang berbeda di tempat yang berbeda. Topik ini membahas urutan pencarian tersebut, aturan penugasan yang menciptakan variabel lokal, serta pernyataan global dan nonlocal beserta konsekuensinya.

Lingkup lokal dan aturan penugasan#

Penugasan di dalam fungsi selalu menciptakan variabel lokal, meskipun nama yang sama sudah ada di lingkup luar. Konsekuensinya, nama yang belum diberi nilai lokal tidak dapat dibaca di dalam fungsi tersebut, sekalipun namanya sudah ada sebagai variabel global.

python Contoh: variabel lokal tidak hidup di luar fungsi
def hitung_luas(sisi):
    luas = sisi * sisi
    return luas

print(hitung_luas(4))

try:
    print(luas)
except NameError as e:
    print(type(e).__name__, str(e))
text Keluaran
16
NameError name 'luas' is not defined

Nama luas hidup hanya selama hitung_luas berjalan, sehingga pembacaannya di luar fungsi ditolak dengan NameError. Aturan ini menjaga badan fungsi tidak berubah perilaku karena variabel luar yang kebetulan bernama sama, dan menjadikan setiap fungsi dapat dibaca secara terpisah.

Urutan pencarian LEGB#

Interpreter mencari nama secara berurutan pada empat tingkat, yaitu lingkup lokal fungsi (Local), fungsi yang melingkupinya (Enclosing), tingkat modul (Global), lalu nama bawaan (Built-in). Pencarian berhenti pada nama pertama yang ditemukan, sehingga tingkat yang lebih dekat selalu menang. Diagram berikut merangkum urutannya.

urutan pencarian namaLLocallingkup fungsi sendiriEEnclosingfungsi melingkupiGGlobaltingkat modulBBuilt-innama bawaan PythonPencarian berhenti pada nama pertama yang ditemukan.
Urutan pencarian nama menurut aturan LEGB

Pembacaan nama pada tingkat global bebas dilakukan tanpa deklarasi apa pun. Contoh berikut menunjukkan fungsi yang membaca variabel modul.

python Contoh: membaca nama pada tingkat global
tingkat_diskon = 0.25

def harga_akhir(harga):
    return harga * (1 - tingkat_diskon)

print(harga_akhir(100))
text Keluaran
75.0

Nama tingkat_diskon tidak ada di dalam fungsi, sehingga ditemukan pada tingkat modul. Hal yang sama berlaku pada fungsi bersarang (nested function), yaitu fungsi yang didefinisikan di dalam fungsi lain, yang membaca nama milik fungsi luarnya.

python Contoh: membaca nama pada lingkup melingkupi
def hitung_diskon():
    tingkat = 0.25

    def terapkan(harga):
        return harga * (1 - tingkat)

    return terapkan(200)

print(hitung_diskon())
text Keluaran
150.0

Fungsi terapkan menemukan tingkat pada lingkup yang melingkupinya tanpa dikirim sebagai parameter. Pola ini menunjukkan tingkat Enclosing bekerja sebelum tingkat Global, dan tingkat Enclosing hanya berlaku bagi fungsi yang berada di dalam hitung_diskon.

Mengubah nama global dengan global#

Membaca nama global bebas dilakukan, tetapi penugasan di dalam fungsi menciptakan nama lokal. Apabila penugasan itu juga membaca nama yang sama, interpreter menolaknya dengan UnboundLocalError karena membaca variabel lokal sebelum nilainya diberikan.

python Contoh: penugasan menciptakan variabel lokal
jumlah_klik = 0

def naikkan():
    try:
        jumlah_klik = jumlah_klik + 1
    except UnboundLocalError:
        print("UnboundLocalError: nama lokal belum diberi nilai")

naikkan()
text Keluaran
UnboundLocalError: nama lokal belum diberi nilai

Baris jumlah_klik = jumlah_klik + 1 menjadikan jumlah_klik lokal, padahal sisi kanan membacanya lebih dahulu. Pernyataan global menyatakan bahwa penugasan pada sebuah nama ditujukan kepada nama global, sehingga fungsi dapat mengubah nilainya.

python Contoh: pernyataan global
jumlah_kunjungan = 0

def catat_kunjungan():
    global jumlah_kunjungan
    jumlah_kunjungan += 1

catat_kunjungan()
catat_kunjungan()
print(jumlah_kunjungan)
text Keluaran
2

Dua pemanggilan menaikkan nilai yang sama, sehingga angka 2 tercetak. Bentuk jumlah_kunjungan += 1 setara dengan penugasan ulang, sehingga tanpa global bentuk itu juga ditolak. Penggunaan global sebaiknya dibatasi, karena fungsi yang mengubah keadaan global menjadi sulit diuji sendirian dan saling ketergantungannya tersembunyi. Alternatif yang lebih bersih adalah mengirim nilai sebagai parameter dan mengembalikan hasilnya kepada pemanggil.

Mengubah nama lingkup luar dengan nonlocal#

Pada fungsi bersarang, pernyataan nonlocal menunjuk nama pada fungsi yang melingkupinya, bukan pada tingkat modul. Bentuk ini dipakai fungsi dalam yang perlu memperbarui keadaan fungsi luarnya tanpa menyentuh variabel global.

python Contoh: pernyataan nonlocal
def buat_penghitung():
    jumlah = 0

    def naik():
        nonlocal jumlah
        jumlah += 1
        return jumlah

    return naik

hitung = buat_penghitung()
print(hitung())
print(hitung())
print(hitung())
text Keluaran
1
2
3

Fungsi naik menaikkan jumlah milik buat_penghitung, sehingga setiap pemanggilan melanjutkan angka sebelumnya. Keadaan itu tetap hidup di antara pemanggilan tanpa satu pun variabel global, dan tidak ada fungsi lain yang dapat mengubahnya karena jumlah hanya milik lingkup itu.

Pernyataan nonlocal menimbulkan SyntaxError saat berkas dibaca apabila nama tidak ditemukan pada fungsi yang melingkupinya. Deklarasinya wajib diletakkan sebelum penugasan pertama pada nama tersebut di dalam fungsi dalam. Aturan ini membatasi jangkauan pernyataannya, sehingga fungsi dalam tidak dapat menyentuh variabel modul lewat nonlocal.

Kesalahan umum#

Kesalahan pertama adalah mengira setiap perubahan terhadap nama global memerlukan global. Pemanggilan metode seperti append mengubah isi objek, bukan mengikat nama baru, sehingga tetap berjalan tanpa deklarasi apa pun.

python Contoh: mengubah isi objek global tanpa global
keranjang = []

def tambah_item(item):
    keranjang.append(item)

tambah_item("buku")
tambah_item("pensil")
print(keranjang)
text Keluaran
['buku', 'pensil']

Yang memerlukan global hanyalah penugasan yang mengganti ikatan nama, misalnya keranjang = [...]. Perbedaan ini menjelaskan mengapa banyak program dapat mengubah isi list global tanpa deklarasi, tetapi gagal begitu menugaskan ulang namanya.

Kesalahan kedua adalah membayangi nama bawaan seperti list atau len dengan variabel lokal yang bernama sama. Pencarian LEGB menemukan nama lokal lebih dahulu, sehingga pemanggilan yang memaksudkan nama bawaan menjadi tidak sah.

python Contoh: nama lokal membayangi nama bawaan
def hitung():
    list = [1, 2, 3]
    return list(range(5))

try:
    print(hitung())
except TypeError as e:
    print(type(e).__name__, str(e))
text Keluaran
TypeError 'list' object is not callable

Di dalam hitung, nama list menunjuk list [1, 2, 3], sehingga upaya memanggilnya sebagai tipe ditolak dengan TypeError. Perbaikannya mengganti nama variabel lokal tersebut agar tidak bertabrakan dengan nama bawaan.

Kesalahan ketiga adalah menyebarkan global pada banyak fungsi untuk berbagi keadaan. Program semacam itu sulit dilacak karena perubahan dapat datang dari mana saja, dan nilainya bergantung pada urutan pemanggilan. Keadaan yang dibagi sebaiknya ditampung pada parameter, nilai kembalian, atau objek yang sifatnya dinyatakan jelas.

Ringkasan#

  • Penugasan di dalam fungsi menciptakan nama lokal, sehingga membaca nama sebelum diberi nilai lokal menimbulkan UnboundLocalError.
  • Pencarian nama mengikuti urutan LEGB, yaitu Local, Enclosing, Global, dan Built-in, dan berhenti pada nama pertama yang ditemukan.
  • Pernyataan global menargetkan penugasan ke tingkat modul, sedangkan nonlocal menargetkan lingkup fungsi yang melingkupinya.
  • Pengubahan isi objek global melalui metodenya tidak memerlukan global, karena ikatan nama yang terlibat tidak berubah.

Referensi#