Lewati ke konten utama

Cara Menggunakan Decorator Python (Dengan Contoh Berbasis Fungsi dan Kelas)

Pelajari decorator Python dengan contoh praktis. Pahami closure, decorator berbasis fungsi dan kelas, serta cara menulis kode yang dapat digunakan ulang dan elegan.
Diperbarui 4 Jun 2026  · 11 mnt baca

Decorator adalah fitur yang kuat dan elegan di Python yang memungkinkan Anda memodifikasi atau memperluas perilaku fungsi dan metode tanpa mengubah kodenya.

Decorator adalah pola desain di Python yang memungkinkan pengguna menambahkan fungsionalitas baru ke objek yang sudah ada tanpa memodifikasi strukturnya. Decorator biasanya diterapkan pada fungsi, dan berperan penting dalam meningkatkan atau memodifikasi perilaku fungsi. Secara tradisional, decorator ditempatkan sebelum definisi fungsi yang ingin Anda dekorasi. Dalam tutorial ini, kami akan menunjukkan cara efektif menggunakan decorator dalam fungsi Python.

Ringkasnya

  • Decorator memungkinkan Anda memodifikasi atau memperluas perilaku fungsi tanpa mengubah kode aslinya
  • Gunakan sintaks @decorator_name untuk menerapkan decorator dengan rapi
  • Selalu gunakan functools.wraps untuk mempertahankan metadata fungsi saat menulis decorator
  • Decorator berbasis kelas memungkinkan perilaku stateful antar pemanggilan fungsi
  • Kasus penggunaan umum mencakup caching (@lru_cache), logging, autentikasi, dan validasi input

Fungsi sebagai Objek Kelas Satu

Fungsi di Python adalah warga kelas satu. Artinya, fungsi mendukung operasi seperti diteruskan sebagai argumen, dikembalikan dari fungsi, dimodifikasi, dan ditetapkan ke variabel. Properti ini krusial karena memungkinkan fungsi diperlakukan seperti objek lain di Python, sehingga memberi fleksibilitas lebih besar dalam pemrograman.

Untuk menjalankan sendiri semua contoh kode dalam tutorial ini dengan mudah, Anda dapat membuat workbook DataLab secara gratis yang sudah terpasang Python dan memuat semua contoh kode. Untuk latihan lebih lanjut tentang decorator, lihat latihan interaktif DataCamp ini.

Menetapkan fungsi ke variabel

Untuk memulai, kita membuat fungsi yang akan menambah satu ke sebuah angka kapan pun dipanggil. Lalu kita tetapkan fungsi tersebut ke variabel dan menggunakan variabel ini untuk memanggil fungsi.

def plus_one(number):
    return number + 1

add_one = plus_one
add_one(5)
6

Mendefinisikan fungsi di dalam fungsi lain 

Mendefinisikan fungsi di dalam fungsi lain adalah fitur yang kuat di Python—dan esensial untuk membangun decorator. Mari lihat ide inti lainnya: meneruskan fungsi sebagai argumen. Ini akan membuat kita selangkah lebih dekat untuk menulis decorator.

def plus_one(number):
    def add_one(number):
        return number + 1


    result = add_one(number)
    return result
plus_one(4)
5

Meneruskan fungsi sebagai argumen ke fungsi lain

Fungsi juga dapat diteruskan sebagai parameter ke fungsi lain. Mari ilustrasikan di bawah ini.

def plus_one(number):
    return number + 1

def function_call(function):
    number_to_add = 5
    return function(number_to_add)

function_call(plus_one)
6

Fungsi yang mengembalikan fungsi lain

Sebuah fungsi juga dapat menghasilkan fungsi lain. Kami akan mencontohkannya di bawah ini.

def hello_function():
    def say_hi():
        return "Hi"
    return say_hi
hello = hello_function()
hello()
'Hi'

Fungsi Dalam dan Closure

Python memungkinkan fungsi bertingkat mengakses ruang lingkup luar dari fungsi yang melingkupinya. Ini adalah konsep penting dalam decorator, yang dikenal sebagai closure.

Closure di Python adalah fungsi yang mengingat lingkungan tempat ia dibuat, bahkan setelah lingkungan tersebut tidak lagi aktif. Artinya, fungsi dalam dapat "menutup" variabel dari ruang lingkup terluarnya dan terus menggunakannya.

Closure penting untuk memahami decorator karena decorator bergantung pada kemampuan fungsi pembungkus (wrapper) bertingkat untuk mengakses dan memodifikasi state dari fungsi decorator yang melingkupinya.

Contoh closure:

def outer_function(message):
    def inner_function():
        print(f"Message from closure: {message}")
    return inner_function

closure_function = outer_function("Hello, closures!")
closure_function()
# Output: Message from closure: Hello, closures!

Pada contoh ini:

  • inner_function adalah closure karena mengakses message, variabel dari ruang lingkup yang melingkupinya (outer_function).
  • Meskipun outer_function telah selesai dieksekusi, inner_function tetap memiliki akses ke message.

Saat Anda membuat decorator, fungsi wrapper (di dalam decorator) adalah closure. Ia mempertahankan akses ke fungsi yang didekorasi dan state atau argumen tambahan apa pun yang didefinisikan dalam fungsi decorator. Contohnya:

def simple_decorator(func):
    def wrapper():
        print("Before the function call")
        func()
        print("After the function call")
    return wrapper

@simple_decorator
def greet():
    print("Hello!")

greet()
# Output:
# Before the function call
# Hello!
# After the function call

Di sini, wrapper adalah closure yang mengingat fungsi greet dan menambahkan perilaku sebelum dan sesudah eksekusinya.

Membuat Decorator Pertama Anda

Sekarang Anda memahami closure—kemampuan suatu fungsi untuk mengingat variabel dari ruang lingkup yang melingkupinya—kita siap membuat decorator pertama yang nyata. Closure adalah "bumbu rahasia" yang memungkinkan decorator bekerja di balik layar.

Mari kita buat decorator sederhana yang akan mengonversi sebuah kalimat menjadi huruf besar. Kita melakukannya dengan mendefinisikan wrapper di dalam fungsi tertutup. Seperti yang Anda lihat, ini sangat mirip dengan fungsi di dalam fungsi lain yang kita buat sebelumnya.

def uppercase_decorator(function):
    def wrapper():
        func = function()
        make_uppercase = func.upper()
        return make_uppercase

    return wrapper

Karena decorator kita menerima fungsi sebagai argumen, kita akan mendefinisikan fungsi baru dan meneruskannya ke decorator. Kita telah belajar sebelumnya bahwa kita dapat menetapkan fungsi ke variabel. Kita akan menggunakan trik itu untuk memanggil fungsi decorator.

def say_hi():
    return 'hello there'

decorate = uppercase_decorator(say_hi)
decorate()
'HELLO THERE'

Menggunakan sintaks @

Namun, Python menyediakan cara yang jauh lebih mudah untuk menerapkan decorator. Kita cukup menggunakan simbol @ sebelum fungsi yang ingin kita dekorasi. Mari perlihatkan praktiknya di bawah ini.

@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi()
'HELLO THERE'

Setelah Anda nyaman menggunakan sintaks @ untuk satu decorator, Anda bisa melangkah lebih jauh dan menumpuk beberapa decorator pada fungsi yang sama. Ingat: urutan itu penting! 

Di bawah ini kita akan mendefinisikan decorator lain yang membagi kalimat menjadi daftar. Lalu kita terapkan decorator uppercase_decorator dan split_string pada satu fungsi.

import functools
def split_string(function):
    @functools.wraps(function)
    def wrapper():
        func = function()
        splitted_string = func.split()
        return splitted_string

    return wrapper 
@split_string
@uppercase_decorator
def say_hi():
    return 'hello there'
say_hi()
['HELLO', 'THERE']

Dari keluaran di atas, kita melihat bahwa penerapan decorator dilakukan dari bawah ke atas. Jika kita menukar urutannya, kita akan melihat error karena list tidak memiliki atribut upper. Kalimat terlebih dahulu dikonversi ke huruf besar lalu dipecah menjadi list.

Catatan: Saat menumpuk decorator, praktik umum adalah menggunakan functools.wraps untuk memastikan metadata fungsi asli dipertahankan sepanjang proses penumpukan. Ini membantu menjaga kejelasan dan konsistensi saat debugging dan memahami properti fungsi yang didekorasi.

Menerima Argumen dalam Decorator

Sejauh ini, kita telah melihat decorator yang hanya membungkus fungsi. Namun bagaimana jika Anda ingin mengonfigurasi decorator itu sendiri—misalnya dengan meneruskan parameter? Di sinilah pabrik decorator (decorator factory) berperan.

def decorator_with_arguments(function):
    def wrapper_accepting_arguments(arg1, arg2):
        print("My arguments are: {0}, {1}".format(arg1,arg2))
        function(arg1, arg2)
    return wrapper_accepting_arguments


@decorator_with_arguments
def cities(city_one, city_two):
    print("Cities I love are {0} and {1}".format(city_one, city_two))

cities("Nairobi", "Accra")

My arguments are: Nairobi, Accra Cities I love are Nairobi and Accra

Catatan: Penting untuk memastikan jumlah argumen pada decorator (
arg1, arg2 pada contoh ini) sesuai dengan jumlah argumen pada fungsi yang dibungkus (cities pada contoh ini). Penyelarasan ini krusial untuk menghindari error dan memastikan fungsionalitas yang tepat saat menggunakan decorator dengan argumen.

Decorator Serbaguna dengan *args dan **kwargs

Untuk mendefinisikan decorator serbaguna yang dapat diterapkan ke fungsi apa pun, kita menggunakan *args dan **kwargs. *args dan **kwargs mengumpulkan semua argumen posisi dan kata kunci dan menyimpannya dalam variabel *args dan **kwargs. *args dan **kwargs memungkinkan kita meneruskan sebanyak mungkin argumen saat pemanggilan fungsi.

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
        print('The positional arguments are', args)
        print('The keyword arguments are', kwargs)
        function_to_decorate(*args)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("No arguments here.")

function_with_no_argument()
The positional arguments are ()
The keyword arguments are {}
No arguments here.

Mari lihat bagaimana kita menggunakan decorator dengan argumen posisi.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
The positional arguments are (1, 2, 3)
The keyword arguments are {}
1 2 3

Berikut cara Anda meneruskan argumen kata kunci ke fungsi yang didekorasi:

@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
    print("This has shown keyword arguments")

function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
The positional arguments are ()
The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
This has shown keyword arguments

Catatan: Penggunaan **kwargs dalam decorator memungkinkannya menangani argumen kata kunci. Ini membuat decorator serbaguna dan mampu menangani berbagai jenis argumen saat pemanggilan fungsi.

Meneruskan Argumen ke Decorator

Sekarang mari lihat bagaimana kita meneruskan argumen ke decorator itu sendiri. Untuk mencapainya, kita mendefinisikan pembuat decorator yang menerima argumen lalu mendefinisikan decorator di dalamnya. Kemudian kita mendefinisikan fungsi wrapper di dalam decorator sebagaimana kita lakukan sebelumnya.

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
    def decorator(func):
        def wrapper(function_arg1, function_arg2, function_arg3) :
            "This is the wrapper function"
            print("The wrapper can access all the variables\n"
                  "\t- from the decorator maker: {0} {1} {2}\n"
                  "\t- from the function call: {3} {4} {5}\n"
                  "and pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,decorator_arg3,
                          function_arg1, function_arg2,function_arg3))
            return func(function_arg1, function_arg2,function_arg3)

        return wrapper

    return decorator

pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
    print("This is the decorated function and it only knows about its arguments: {0}"
           " {1}" " {2}".format(function_arg1, function_arg2,function_arg3))

decorated_function_with_arguments(pandas, "Science", "Tools")
The wrapper can access all the variables
    - from the decorator maker: Pandas Numpy Scikit-learn
    - from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools

Mendebug Decorator

Seperti yang telah kita lihat, decorator membungkus fungsi. Nama fungsi asli, docstring, dan daftar parameternya semuanya tersembunyi oleh closure wrapper: Misalnya, ketika kita mencoba mengakses metadata decorated_function_with_arguments, kita akan melihat metadata milik closure wrapper. Ini menjadi tantangan saat debugging.

decorated_function_with_arguments.__name__
'wrapper'
decorated_function_with_arguments.__doc__
'This is the wrapper function'

Untuk mengatasi tantangan ini Python menyediakan functools.wraps decorator. Decorator ini menyalin metadata yang hilang dari fungsi yang tidak didekorasi ke closure yang didekorasi. Mari kita tunjukkan caranya.

import functools

def uppercase_decorator(func):
    @functools.wraps(func)
    def wrapper():
        return func().upper()
    return wrapper
@uppercase_decorator
def say_hi():
    "This will say hi"
    return 'hello there'

say_hi()
'HELLO THERE'

Sekarang, ketika kita memeriksa metadata say_hi, kita melihat bahwa metadata tersebut dengan benar merefleksikan fungsi asli—bukan wrapper.

say_hi.__name__
'say_hi'
say_hi.__doc__
'This will say hi'

Sangat dianjurkan dan merupakan praktik yang baik untuk selalu menggunakan functools.wraps saat mendefinisikan decorator. Ini akan menghemat banyak kerepotan saat debugging.

Decorator Berbasis Kelas

Meski decorator berbasis fungsi umum digunakan, Python juga memungkinkan Anda membuat decorator berbasis kelas, yang memberikan fleksibilitas dan keterawatan lebih tinggi, terutama untuk kasus yang kompleks. Jika Anda baru dalam pemrograman berorientasi objek di Python, meninjau dasar-dasarnya akan membantu Anda memahami bagian ini dengan lebih baik. Decorator berbasis kelas adalah kelas dengan metode __call__ yang memungkinkannya berperilaku seperti fungsi.

class UppercaseDecorator:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        result = self.function(*args, **kwargs)
        return result.upper()

@UppercaseDecorator
def greet():
    return "hello there"

print(greet())
# Output: HELLO THERE

Cara kerjanya:

  1. Metode __init__ menginisialisasi decorator dengan fungsi yang akan didekorasi.
  2. Metode __call__ dipanggil ketika fungsi yang didekorasi dipanggil, sehingga decorator dapat memodifikasi perilakunya.

Keunggulan decorator berbasis kelas:

  • Decorator stateful: Decorator berbasis kelas dapat mempertahankan state menggunakan variabel instans, tidak seperti decorator berbasis fungsi yang memerlukan closure atau variabel global.
  • Keterbacaan: Untuk decorator yang kompleks, mengenkapsulasi logika dalam kelas dapat membuat kode lebih teratur dan mudah dipahami.

Contoh decorator stateful:

class CallCounter:
    def __init__(self, function):
        self.function = function
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Function {self.function.__name__} has been called {self.count} times.")
        return self.function(*args, **kwargs)

@CallCounter
def say_hello():
    print("Hello!")

say_hello()
say_hello()
# Output:
# Function say_hello has been called 1 times.
# Hello!
# Function say_hello has been called 2 times.
# Hello!

Kasus Penggunaan Decorator di Dunia Nyata: Caching

Decorator lru_cache adalah alat bawaan di Python yang menyimpan hasil pemanggilan fungsi yang mahal. Ini meningkatkan kinerja dengan menghindari perhitungan berulang untuk input yang sama.

Contoh:

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # Subsequent calls with the same argument are much faster

Penggunaan umum lainnya untuk decorator:

  • Logging: Melacak pemanggilan fungsi, argumen, dan nilai kembalian untuk debugging atau audit.

  • Autentikasi: Menerapkan kontrol akses dalam aplikasi web seperti Flask atau Django.

  • Pengukuran waktu eksekusi: Mengukur dan mengoptimalkan waktu eksekusi fungsi untuk tugas yang kritis terhadap performa.

  • Mekanisme retry: Secara otomatis mencoba ulang pemanggilan fungsi yang gagal, berguna dalam operasi jaringan.

  • Validasi input: Memvalidasi argumen fungsi sebelum eksekusi.

Ringkasan Decorator Python

Decorator secara dinamis mengubah fungsionalitas fungsi, metode, atau kelas tanpa harus langsung menggunakan subclass atau mengubah kode sumber fungsi yang didekorasi. Menggunakan decorator di Python juga memastikan kode Anda DRY (Don't Repeat Yourself). Decorator memiliki beberapa kasus penggunaan seperti:

  • Otorisasi dalam framework Python seperti Flask dan Django
  • Logging
  • Mengukur waktu eksekusi
  • Sinkronisasi

Untuk mempelajari lebih lanjut tentang decorator Python, lihat Decorator Library Python.

FAQs

Apakah ada pertimbangan performa saat menggunakan decorator?

Ya, decorator dapat menambah overhead karena memperkenalkan pemanggilan fungsi tambahan. Saat kinerja sangat penting, penting untuk mempertimbangkan overhead ini, terutama jika fungsi yang didekorasi sering dipanggil dalam konteks yang sensitif terhadap performa.

Bisakah decorator digunakan dengan method kelas, dan bagaimana caranya?

Ya, decorator dapat diterapkan pada method kelas seperti halnya fungsi biasa. Decorator akan menerima method sebagai argumen dan mengembalikan method baru atau versi yang dimodifikasi. Ini umum digunakan untuk logging, kontrol akses, atau menegakkan prasyarat.

Bagaimana decorator dapat digunakan untuk keperluan logging?

Decorator dapat digunakan untuk mencatat pemanggilan fungsi, argumen, dan nilai kembalian dengan membungkus eksekusi fungsi menggunakan kode yang merekam detail tersebut ke sistem logging. Ini membantu pelacakan dan debugging.

Apa makna simbol @ dalam decorator?

Simbol @ adalah pemanis sintaks di Python yang menyederhanakan penerapan decorator pada fungsi. Ini memungkinkan Anda menerapkan decorator tepat di atas definisi fungsi, membuat kode lebih bersih dan mudah dibaca.

Bisakah decorator memodifikasi nilai kembalian fungsi, dan bagaimana caranya?

Ya, decorator dapat memodifikasi nilai kembalian sebuah fungsi dengan mengubah pernyataan return di dalam fungsi wrapper. Misalnya, decorator dapat mengubah tipe data keluaran, memformatnya, atau menambahkan pemrosesan tambahan sebelum mengembalikan hasil akhir.

Bagaimana Python menangani ruang lingkup variabel saat fungsi bertingkat mengakses variabel dari fungsi yang melingkupinya?

Python menggunakan aturan ruang lingkup LEGB (Local, Enclosing, Global, Built-in). Dalam kasus fungsi bertingkat, fungsi di dalam dapat mengakses variabel dari ruang lingkup fungsi yang melingkupinya, yang memungkinkan terjadinya closure di mana fungsi dalam mempertahankan akses ke variabel fungsi luar bahkan setelah fungsi luar selesai dieksekusi.

Apa perbedaan antara closure dan decorator?

Closure adalah fungsi yang mengingat variabel dari ruang lingkup luarnya. Decorator adalah fungsi yang menggunakan closure untuk meningkatkan atau membungkus fungsi lain.

Kapan saya harus menggunakan functools.wraps?

Selalu gunakan functools.wraps saat menulis decorator. Ini mempertahankan metadata fungsi asli (seperti nama dan docstring), yang bermanfaat untuk debugging dan dokumentasi.

Bisakah decorator menerima argumen?

Tentu! Anda dapat meneruskan argumen ke decorator dengan membungkusnya dalam fungsi lain (pabrik decorator). Ini memungkinkan Anda menyesuaikan perilaku decorator.

Apa keunggulan decorator berbasis kelas?

Decorator berbasis kelas memungkinkan Anda mempertahankan state di antara pemanggilan fungsi dan mengorganisasi logika yang lebih kompleks dengan cara berorientasi objek.

Apakah decorator hanya untuk fungsi?

Tidak. Anda juga dapat menggunakan decorator pada kelas dan method. Dalam framework seperti Flask dan Django, decorator umum digunakan pada rute, view, dan model.

Topik

Pelajari lebih lanjut tentang Python

Kursus

Pengantar Fungsi di Python

3 Hr
465.9K
Pelajari seni menulis fungsi sendiri dalam Python, serta konsep-konsep penting seperti lingkup (scoping) dan penanganan kesalahan (error handling).
Lihat DetailRight Arrow
Mulai Kursus
Lihat Lebih BanyakRight Arrow