Soal 1Manakah dari berikut ini yang memiliki makna yang sama dengan menempatkan @logger di atas def greet(): ...?
Decorator — Menambahkan Perilaku ke Fungsi dengan @
Pelajari decorator Python dari dasar. Cakup sintaks @decorator, bentuk def yang mengembalikan inner, dan contoh nyatanya di framework.
Di akhir artikel sebelumnya tentang lambda, kamu sudah melihat cara utama untuk memperlakukan fungsi sebagai nilai. Untuk menutup, mari kita pin down decorator — sintaks khusus untuk melapisi perilaku tambahan di atas sebuah fungsi.
Apa Itu Decorator?
Sebuah decorator adalah cara untuk menambahkan perilaku sebelum dan sesudah fungsi tanpa mengubah fungsi itu sendiri. Hal-hal seperti "catat pemanggilan," "ukur waktu eksekusi," atau "cache hasilnya" — perilaku bersama yang ingin kamu lapisi ke banyak fungsi — bisa hidup di satu tempat.
Sintaksnya hanya satu baris di atas definisi fungsi: @nama_decorator. Python membacanya sebagai "sama dengan func = nama_decorator(func)".
@logger di atas def greet(): membuat Python menjalankan greet = logger(greet) secara internal, mengganti greet dengan fungsi baru yang dibungkus oleh logger.# Decorator itu sendiri (higher-order function yang menerima dan mengembalikan fungsi)
def logger(func):
def wrapper():
print("=== mulai ===")
func() # panggil fungsi asli
print("=== selesai ===")
return wrapper
# Sisi pemanggil: cukup tambahkan @
@logger
def greet(): # → logger(greet)
print("Halo")
greet()
# === mulai ===
# Halo
# === selesai ===
# Setara secara internal dengan:
# def greet():
# print("Halo")
# greet = logger(greet)
Decorator Dasar — Membungkus Fungsi dengan wrapper
Kerangka decorator adalah bentuk 3 langkah: fungsi luar menerima func, inner function (konvensional wrapper) memanggil func(), dan kamu return wrapper. wrapper yang menyimpan func agar tetap tersedia ketika ia berjalan adalah persis sebuah closure.
Apa pun yang kamu tulis sebelum dan sesudah func() berjalan setiap kali fungsi yang didekorasi dipanggil.
def logger(func):
def wrapper(*args, **kwargs):
print(f"[LOG] menjalankan {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] {func.__name__} selesai")
return result
return wrapper
@logger
def greet(name):
return f"Halo, {name}"
print(greet("Budi"))
# [LOG] menjalankan greet
# [LOG] greet selesai
# Halo, Budi
- greet diganti dengan fungsi wrapper
- Badan
greetasli tetap hidup sebagaifuncdi dalamwrapper
funcmenyimpangreetasli- Membangun
wrapperdi dalam dan mengembalikannya
- Menjalankan pre →
func()→ post berurutan - Dari luar, ini adalah
greetyang baru
Teruskan Argumen Apa Pun dengan *args / **kwargs
Sejauh ini, wrapper tidak menerima argumen. Saat kamu ingin mendekorasi fungsi yang memang menerima argumen, gunakan *args / **kwargs untuk menerima argumen apa pun apa adanya dan meneruskannya langsung ke `func`.
Itu mengubah decorator menjadi decorator generik yang bekerja dengan signature fungsi apa pun. Ia menangani add(2, 3) (posisional) dan add(2, 3, name="ABC") (keyword) dengan decorator yang sama.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"call: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs) # unpack dan teruskan di sini juga
print(f"result: {result}")
return result # jangan lupa kembalikan
return wrapper
@log_call
def add(a, b):
return a + b
print(add(2, 3))
# call: args=(2, 3), kwargs={}
# result: 5
# 5
print(add(2, b=3))
# call: args=(2,), kwargs={'b': 3}
# result: 5
# 5
Jangan Lupa return Hasilnya
Jika wrapper hanya menulis result = func(...) dan melupakan return, nilai kembali fungsi yang didekorasi diam-diam berubah menjadi None. Kecelakaan klasiknya adalah menemukan add(2, 3) diam-diam mengembalikan None — saat kamu menulis decorator, perlakukan return result sebagai bagian dari ingatan otot yang sama.
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Apa yang dicetak kode ini?def deco(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return wrapper
@deco
def plus(a, b):
return a + b
print(plus(3, 4))