Soal 1Apa yang dicetak kode ini?stock = 100
def f():
stock = 50
f()
print(stock)
Inner Function dan Closure — Menguasai Scope dengan global dan nonlocal
Pelajari scope, inner function, dan closure di Python untuk menguasai aliran variabel di dalam fungsi.
Pada artikel sebelumnya kamu sudah melihat bagaimana argumen dan nilai kembalian berperilaku. Kali ini kamu akan menggali beberapa topik lain seputar fungsi: bagaimana Python memisahkan variabel di dalam dan di luar fungsi (scope), cara mendefinisikan fungsi di dalam fungsi (inner function), dan cara mengembalikan fungsi yang mengingat nilai luar (sebuah closure). Sambil jalan, kamu akan tahu kapan menggunakan global dan nonlocal untuk menulis ulang variabel di scope luar dari dalam fungsi.
Variabel Global dan Local — Scope Itu Terpisah
Variabel yang didefinisikan di luar fungsi adalah variabel global; variabel yang didefinisikan di dalam fungsi adalah variabel local. Kamu bisa membaca variabel global dari dalam fungsi, tetapi jika kamu menetapkan nilai ke nama yang sama dengan = value di dalam fungsi, Python membuat variabel local baru — sesuatu yang terpisah dari yang di luar.
Jika kamu memeriksa alamat memori variabel dengan id(), kamu akan melihat bahwa yang di luar dan yang di dalam menunjuk ke lokasi yang berbeda.
stock = 100 # variabel global
def show_stock():
print(f"di dalam: {stock}") # 100 — membaca nilai luar
def try_change():
stock = 50 # membuat variabel local yang baru
print(f"di dalam: {stock}") # 50
show_stock() # di dalam: 100
try_change() # di dalam: 50
print(f"di luar: {stock}") # di luar: 100 ← yang di luar tidak berubah
- stock = 100 — variabel global
- Fungsi hanya bisa membaca dari dalam
print(stock)→ membaca nilai luar 100- Tidak membuat variabel local
stock = 50— membuat local baru di dalam fungsi- Tidak memengaruhi
stockdi luar
Menulis Ulang Variabel Luar dengan Keyword global
Saat kamu memang ingin menulis ulang variabel global luar dari dalam fungsi, deklarasikan global nama_variabel di awal fungsi. Itu memberi tahu Python, "ini adalah global yang di luar, bukan local baru." Tanpa itu, penetapan seperti count += 1 mencampur pembacaan dan penulisan pada nama yang sama dan kamu akan mendapatkan UnboundLocalError ("local variable referenced before assignment").
visit_count = 0
# × Tanpa global → UnboundLocalError
# def increment():
# visit_count += 1
# increment() ← UnboundLocalError
# ○ global memungkinkan kamu menulis ulang global luar
def increment():
global visit_count
visit_count += 1
increment()
increment()
increment()
print(visit_count) # 3
Pakai global Seminimal Mungkin
global membuat fungsi diam-diam menulis ulang state luar, sehingga satu penulisan buruk yang berjarak 100 baris menjadi sangat sulit dilacak pada skala besar.
Utamakan mengembalikan nilai dengan return dan menetapkannya di sisi pemanggil, dan gunakan class (akan dibahas nanti) ketika kamu memang butuh perilaku berstate.
Bagaimana Fungsi dan Variabel Hidup di Memori — Nama vs. Objek
Secara internal, Python menyimpan namespace — pencarian "nama → objek". x = 5 berarti "buat integer 5 di memori dan buat nama x menunjuk ke sana."
def f(): ... bekerja dengan cara yang sama: ia membangun sebuah objek fungsi (badan fungsi) di memori dan membuat nama f menunjuk ke sana. Hanya ada satu pencarian seperti ini per program — global namespace — disiapkan saat module dimuat dan tetap ada sampai program keluar.
- x = 5 — nama
xmenunjuk ke integer 5 - def f — nama
fmenunjuk ke objek fungsi - Tetap hidup sampai program keluar
- Menyimpan argumen dan local
- Dibuang seluruhnya saat return
- Sepenuhnya terpisah dari yang pertama
- Local-nya independen
def sekali dan fungsi terdaftar di global namespace, di mana ia tetap ada sampai keluar. Namun setiap panggilan membangun frame local-nya sendiri dan membuangnya saat return. Inner function, closure, dan nonlocal yang dibahas berikutnya semua dibangun di atas struktur frame ini.Inner Function — Mendefinisikan Fungsi di Dalam Fungsi
Tumpuk satu lagi def di dalam fungsi dan kamu mendapat inner function — fungsi yang hanya ada untuk dipakai di dalam fungsi luar. Itu cara yang bagus untuk menamai potongan logika yang bermakna di dalam fungsi panjang sehingga badan fungsi terbaca sebagai urutan langkah yang rapi.
Karena inner function tidak terjangkau dari luar, ia juga cocok untuk menyembunyikan helper yang tidak ingin kamu ekspos.
- Memanggil
validatedari sini → NameError - Tidak ada di luar
- Menyimpan argumen
name/age - Bisa memanggil
validatedari dalam
- Membaca
name/ageluar - Tidak terekspos ke luar
def process_user(name, age):
def validate():
if not name or not isinstance(age, int) or age < 0:
raise ValueError("Input tidak valid")
validate() # panggil inner function
print(f"Diproses: {name} ({age})")
process_user("Budi", 25)
# Diproses: Budi (25)
# validate tidak terjangkau dari luar process_user
# validate() ← NameError
Bagus Saat Kamu Ingin Memecah Fungsi Panjang
Begitu fungsi melewati 50 atau 100 baris, memecahnya menjadi inner function yang dinamai untuk tiap potongan bermakna — process_name() / process_age() dan seterusnya — mengubah fungsi luar menjadi daftar langkah yang mudah dibaca. Jika nantinya kamu ingin memakai ulang salah satunya di luar, mempromosikan inner function menjadi fungsi biasa itu sepele.
Closure — Fungsi yang Mengingat Nilai Luar
Inner function bisa membaca argumen dan local fungsi luar. Ambil satu langkah lebih jauh — minta fungsi luar return inner function — dan kamu sudah membangun fungsi yang terus bekerja dengan nilai luar yang sudah tertanam. Itulah closure.
Ini cara rapi untuk memproduksi massal fungsi serupa yang hanya berbeda pada satu pengaturan, seperti "fungsi yang melipattigakan" dan "fungsi yang melipatlimakan". Ambil pengaturan itu (factor) sebagai argumen luar dan referensikan dari inner function: make_multiplier(3) mengembalikan "sebuah multiply yang mengingat 3", dan make_multiplier(5) mengembalikan "sebuah multiply yang mengingat 5".
def make_multiplier(factor):
def multiply(x):
return x * factor # mereferensikan factor luar
return multiply
times3 = make_multiplier(3) # fungsi yang mengingat factor=3
times5 = make_multiplier(5) # fungsi terpisah yang mengingat factor=5
print(times3(10)) # 30
print(times5(10)) # 50
print(times3(7)) # 21
Closure Memberi "Fungsi Terkonfigurasi-di-Awal"
Saat kamu ingin membuat banyak perhitungan serupa yang hanya berbeda pada satu pengaturan — "pajak 10%" vs "pajak 8%" dan seterusnya — closure bersinar. Alih-alih meneruskan pengaturan di setiap panggilan, kamu memberikan satu fungsi yang sudah terkonfigurasi, dan kode pemanggil tetap jauh lebih bersih.
nonlocal — Menulis Ulang Variabel Fungsi yang Membungkus
Closure bisa membaca variabel luar dengan baik, tapi seperti pada global, mencoba menulis ulang-nya melalui count += 1 akan meledak dengan UnboundLocalError. Keyword yang mengizinkan penulisan ulang itu adalah nonlocal. Jika global menargetkan level module, nonlocal menargetkan local fungsi yang langsung membungkus.
Bungkus State di Dalam Objek Fungsi
nonlocal adalah cara kanonis untuk membungkus counter yang bertambah pada setiap panggilan di dalam sebuah fungsi.
Tidak seperti global, state tetap tertutup di dalam objek fungsi tertentu, sehingga efek samping tidak menyebar, dan kamu mendapatkan state yang lebih aman daripada menggunakan global.
def create_counter():
x = 0
def increment():
nonlocal x # mendeklarasikan kita memperbarui x dari create_counter
x += 1
return x
return increment
counter = create_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
# Counter kedua memiliki x sendiri yang independen
counter2 = create_counter()
print(counter2()) # 1 — tidak berhubungan dengan counter
counter = create_counter()— menerimaincrement- Kode di luar tidak bisa menyentuh
xsecara langsung
- x = 0 — counter, dibuat tepat sekali
- Yang dirujuk
incrementmelaluinonlocal
nonlocal x— menunjuk kexluarx += 1memperbarui x luar
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Mengapa kode ini error? Pilih penjelasan terbaik.count = 0
def inc():
count += 1
inc()
Soal 3Apa yang dicetak print(times3(10))?def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
times3 = make_multiplier(3)