Belajar dengan membaca secara berurutan

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)".

@ Berarti "Jalankan Fungsi Ini Melalui Fungsi Itu"
@loggerdef greet():greet = logger(greet)berkembang menjadi
Menulis @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
Bagaimana Decorator logger Membungkus Sebuah Fungsi
Module (Global Namespace)
  • greet diganti dengan fungsi wrapper
  • Badan greet asli tetap hidup sebagai func di dalam wrapper
Frame logger(func)
  • func menyimpan greet asli
  • Membangun wrapper di dalam dan mengembalikannya
wrapper (closure yang mengingat func)
  • Menjalankan pre → func() → post berurutan
  • Dari luar, ini adalah greet yang baru
Apa yang dilakukan @logger adalah menukar greet global dengan fungsi berbeda bernama wrapper. Badan greet asli dipanggil sebagai func dari dalam wrapper.

Bangun decorator bracket yang mencetak sapaan sebelum dan sesudah fungsi dan lapiskan padanya.

① Definisikan def bracket(func):, dan di dalamnya def wrapper():. Buat wrapper menjalankan print("--- mulai ---")func()print("--- selesai ---") berurutan, dan buat fungsi luar mengembalikan wrapper.

② Definisikan def introduce(): yang didekorasi dengan @bracket, dengan hanya print("Saya Budi") di badannya.

③ Panggil introduce() dan pastikan badan diapit di antara --- mulai --- / --- selesai ---.

(Saat jawaban benar, penjelasan akan muncul.)

Python Editor

Jalankan kode untuk melihat output

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
Bagaimana *args / **kwargs Meneruskan Argumen Langsung
add(2, b=3)wrapper(*args, **kwargs)args=(2,), kwargs={'b': 3}unpack dan teruskansebagai func(*args, **kwargs)add(a, b)add(a=2, b=3) aslikembalikan 5wrapper kembalikanapa adanyaterimalangkah-preunpackhitungkirim hasil kembali

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.

Bangun decorator log_call yang mencetak setiap pemanggilan dan terapkan ke fungsi 2 argumen.

① Definisikan def log_call(func):, dan di dalamnya def wrapper(*args, **kwargs):.

② Di wrapper, cetak f"call: args={args}, kwargs={kwargs}", lalu result = func(*args, **kwargs), lalu return result.

③ Buat fungsi luar mengembalikan wrapper.

④ Definisikan def multiply(a, b): return a * b yang didekorasi dengan @log_call. Panggil print(multiply(4, 5)) dan print(multiply(2, b=10)) dan pastikan log muncul diikuti nilai kembali.

Python Editor

Jalankan kode untuk melihat output
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Manakah dari berikut ini yang memiliki makna yang sama dengan menempatkan @logger di atas def greet(): ...?

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))