Pada artikel ini, kita akan mempelajari tentang decorator, cara membuat decorator, dan kapan kita sebaiknya menggunakan decorator.
Tabel Konten
- Pengertian Decorator
- Syarat Sebelum Mempelajari Decorator
- Kembali ke Decorator
- Dekorasi Fungsi Dengan Parameter
- Menggabungkan Decorator di Python
Pengertian Decorator
Python memiliki sebuah fitur menarik bernama decorator yang berfungsi menambah fungsionalitas pada kode program.
Hal ini disebut juga metaprogramming karena ada bagian dari program yang mencoba untuk memodifikasi bagian lainnya pada saat eksekusi.
Syarat Sebelum Mempelajari Decorator
Sebelum dapat memahami tentang decorator, kita harus terlebih dahulu memahami dasar – dasar Python.
Kita harus paham terlebih dahulu bahwa semua yang ada di Python adalah objek (termasuk kelas). Nama pengenal, seperti variabel yang kita deklarasikan merujuk kepada objek tersebut. Begitu juga dengan fungsi. Fungsi adalah termasuk objek juga. Satu objek bisa memiliki banyak pengenal (identifier) yang merujuk kepadanya.
Perhatikan contoh berikut
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
Pada saat kode di atas dijalankan, kedua fungsi first
dan second
menampilkan output yang sama. Di sini, variabel first
dan second
merujuk pada objek fungsi yang sama.
Sekarang mari kita tinjau hal yang lain. Sebuah fungsi bisa dijadikan sebagai argumen dari fungsi yang lain.
Bila Anda sudah pernah menggunakan fungsi seperti map
, filter
, dan reduce
di Python, maka Anda sudah tahu tentang hal ini.
Fungsi yang menjadikan fungsi lain sebagai argumen disebut juga fungsi dengan orde yang lebih tinggi. Berikut adalah contohnya:
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
Kita bisa memanggil fungsi tersebut seperti berikut:
>>> operate(inc, 3) 4 >>> operate(dec, 3) 2
Lebih lanjut lagi, sebuah fungsi bisa mengembalikan fungsi lain.
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
#Outputs "Hello"
new()
Pada contoh tersebut, is_returned()
adalah fungsi bersarang yang didefinisikan dan dikembalikan tiap kali fungsi is_called()
dipanggil.
Selain memahami tentang hal di atas, kita juga sudah harus paham tentang python closure
Kembali ke Decorator
Sebuah decorator mengambil fungsi sebagai argumennya, menambahkan beberapa hal, dan kemudian mengembalikannya.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
Bila kode di atas kita jalankan pada mode interaktif, maka hasilnya adalah seperti berikut:
>>> ordinary() I am ordinary >>> # Mari kita buat decorator dari fungsi ordinary >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary
Pada contoh di atas, make_pretty()
adalah sebuah decorator.
Pada baris perintah pretty = make_pretty(ordinary)
, fungsi ordinary
didekorasi dan fungsi kembaliannya diberi nama pretty
.
Kita bisa lihat bahwa fungsi decorator menambahkan beberapa fungsionalitas ke fungsi asli. Hal ini mirip dengan pengemasan kado. Decorator bertindak sebagai bungkusnya. Objek yang didekorasi (isi kado) tidak berubah. Akan tetapi, ketika dibungkus, akan terlihat lebih bagus (karena didekorasi).
Umumnya, kita mendekorasi fungsi dan menyimpannya ke variabel seperti berikut:
ordinary = make_pretty(ordinary)
Python memiliki sintaks yang lebih sederhana untuk penulisan fungsi di atas yaitu dengan menggunakan tanda @
diikuti oleh nama fungsi decorator. Kita menempatkannya di atas fungsi yang akan didekorasi. Maka contoh berikut ini:
@make_pretty def ordinary(): print("I am ordinary")
adalah sama dengan yang berikut:
def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)
Cara yang menggunakan tanda @
di atas adalah sintaks yang lebih elegan untuk mengimplementasikan decorator.
Dekorasi Fungsi Dengan Parameter
Decorator di atas sangat sederhana dan hanya berlaku untuk fungsi yang tidak memiliki parameter. Bagaimana jika fungsi yang akan didekorasi memiliki argumen seperti berikut?
def divide(a, b): return a/b
Fungsi tersebut memiliki 2 parameter, a
dan b
. Bila kita melewatkan nilai 0 ke b
, maka akan terjadi error ZeroDivisionError
.
>>> divide(2, 5) 0.4 >>> divide(2, 0) Traceback (most recent call last): ... ZeroDivisionError: division by zero
Sekarang kita akan membuat decorator untuk mengecek penyebab error ini.
def smart_divide(func):
def inner(a,b):
print("Saya akan membagi",a,"dan",b)
if b == 0:
print("Whoops! tidak bisa membagi dengan 0")
return
return func(a,b)
return inner
@smart_divide
def divide(a,b):
return a/b
Kode yang menggunakan decorator ini akan mengembalikan None
jika terjadi error.
>>> divide(2, 5) Saya akan membagi 2 dan 5 0.4 >>> divide(2, 0) Saya akan membagi 2 dan 0 Whoops! tidak bisa membagi dengan 0
Dengan cara tersebut kita bisa mendekorasi fungsi yang memiliki parameter.
Bila kita perhatikan dengan baik, kita akan melihat kalau semua parameter dari fungsi yang di dalam decorator akan menjadi parameter dari fungsi yang didekorasi. Dengan itu, kita bisa membuat decorator yang lebih umum yang dapat bekerja dengan berapapun jumlah parameternya.
Di Python, hal ini dapat dilakukan dengan sintaks function(*args, **kwargs)
di mana args
adalah tuple dari argumen – argumen berdasarkan posisi, dan kwargs
adalah dictionary dari argumen – argumen ber-keyword.
Berikut ini adalah contoh fungsi inner()
yang memiliki sembarang parameter didekorasi oleh fungsi works_for_all()
.
def works_for_all(func): def inner(*args, **kwargs): print("Saya bisa mendekorasi fungsi apa saja") return func(*args, **kwargs) return inner()
Menggabungkan Decorator di Python
Beberapa decorator bisa dihubungkan atau digabungkan. Sebuah fungsi bisa didekorasi beberapa kali dengan satu atau beberapa fungsi decorator. Kita meletakkan decorator di atas fungsi yang akan didekorasi. Untuk jelasnya perhatikan contoh berikut:
def star(func):
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
printer("Hello")
Outputnya akan tampak seperti berikut:
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
Kode decorator di atas yaitu:
@star @percent def printer(msg): print(msg)
adalah sama dengan:
def printer(msg): print(msg) printer = star(percent(printer))
Urutan dari decorator perlu diperhatikan. Bila kita mengubah urutannya menjadi:
@percent @star def printer(msg): print(msg)
maka hasilnya akan menjadi:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%