Python Decorator
Decorator merupakan komponen penting dalam Python yang mendukung fleksibilitas dalam manipulasi fungsi. Per definisi, decorator adalah fungsi yang melakukan operasi terhadap fungsi lain dan memodifikasi perilakunya tanpa harus mengubah secara eksplisit.
Decorator bekerja menggunakan prinsip metaprogramming karena ia akan memodifikasi bagian program lainnya pada saat eksekusi. Secara konsep, decorator menggunakan metode inner function dan Python closure. Sebelum membahas lebih lanjut tentang decorator, berikut ini akan dibahas beberapa istilah dan komponen Python yang berkaitan.
Fungsi
Dalam Python, fungsi digunakan untuk mengatur alur/logika dalam program.
Fungsi dibuat untuk melakukan aksi tertentu dan dapat dipanggil
kapanpun fungsi tersebut dibutuhkan. Fungsi pada Python didefinisikan dengan
menggunakan kata kunci def
kemudian diikuti dengan nama fungsinya seperti pada
contoh berikut ini:
def nama_fungsi():
print("Ini adalah Fungsi")
Fungsi dapat dipanggil dengan menuliskan nama fungsi berikut paramaternya.
Untuk contoh di atas, karena didefinisikan tanpa parameter maka fungsi tersebut
dipanggil tanpa menggunakan argumen: nama_fungsi()
.
Pemanggilan nama fungsi diikuti dengan tanda kurung (parentheses) akan
mengeksekusi fungsi tersebut. Pada contoh di atas,
fungsi akan mencetak Ini adalah Fungsi
.
Fungsi dengan Parameter
Fungsi dapat menerima parameter berupa nilai yang yang akan menentukan keluaran fungsi tersebut. Nilai parameter adalah input dari luar yang akan diproses secara internal. Nilai parameter saat pemanggilan fungsi ini disebut juga sebagai argumen.
Terdapat beberapa jenis argumen fungsi yang bisa digunakan yaitu: argumen wajib (required argument), argumen default dan argumen dengan jumlah bervariasi.
Fungsi dengan argumen wajib mengharuskan pengguna untuk memberikan
argumen dengan jumlah sesuai dengan yang telah ditentukan (pada
parameter fungsi). Pada contoh berikut terdapat sebuah parameter
dan disebut nama
. Parameter nama
bisa menerima argumen
berupa string, integer, fungsi atau tipe data lain.
def halo_nama(nama):
print(nama)
halo_nama('nyayu')
Pengguna juga dapat memasukkan keyword pada argumen:
def halo_nama(nama):
print(nama)
halo_nama(nama='nyayu')
Argumen default berguna untuk memberikan nilai default pada parameter. Nilai default ini akan dipakai apabila fungsi dieksekusi tanpa menggunakan argumen.
Apabila fungsi dipanggil menggunakan argumen, maka fungsi akan menghasilkan output sesuai dengan argumen yang dimasukkan pengguna.
def halo_nama(nama='Nyayu'):
print(nama)
halo_nama()
halo_nama('Nias')
Argumen dengan jumlah bervariasi digunakan apabila jumlah parameter
tidak bisa ditentukan secara pasti. Untuk ini digunakan parameter
dengan keyword *args
dan **kwargs
, sehingga parameter
menjadi bersifat dinamis. Sintaks *args
digunakan untuk melewatkan
sembarang jumlah argumen ke fungsi. Dengan simbol *
di depan parameter
akan menyebabkan fungsi menerima sejumlah argumen yang akan
dikonsolidasikan ke dalam sebuah list.
Sedangkan sintaks **kwargs
digunakan untuk melewatkan sembarang jumlah argumen dengan
menggunakan keyword (pasangan key-value - seperti pada
dictionary) ke dalam fungsi. Simbol **
berfungsi untuk
mengubah semua argumen keyword menjadi dictionary.
Penamaan sintaks *arg
s dan *kwargs
bukan merupakan sesuatu yang baku
tetapi umum digunakan karena args merepresentasikan arguments
dan
kwargs berarti keyword arguments
. Pengguna dapat menggunakan
penamaan lain seperti : *nama, **knama, **kbuku dan sebagainya.
def halo_nama(*args,**kwargs):
print([i for i in args])
print({k:v for k,v in kwargs.items()})
halo_nama('a',1,5,'bc',a=2,b='abc')
Fungsi sebagai First-Class Object
Yang dimaksud fungsi sebagai first-class object adalah fungsi dapat disimpan dalam struktur data, dijadikan argumen atau dijadikan return value dari fungsi lain. Karakteristik ini penting, karena decorator akan menggunakan fungsi sebagai argumennya, menambahkan beberapa hal terhadap fungsi tersebut dan selanjutnya dikembalikan menjadi fungsi yang telah dimodifikasi. Berikut ini contoh dua buah fungsi untuk menjumlahkan dua buah bilangan:
def jumlah(a,b):
return a+b
def Jumlah10dan2(fungsijumlah):
return fungsijumlah(10,2)
Jumlah10dan2(jumlah)
Fungsi pertama merupakan fungsi yang menerima dua buah bilangan dan
menjumlahkan bilangan tersebut. Sedangkan fungsi kedua
Jumlah10dan2
, merupakan fungsi yang secara spesifik menjumlahkan angka 10
dan 2. Berbeda dengan fungsi pertama yang menerima argumen dalam bentuk
bilangan, fungsi Jumlah10dan2
menerima argumen dalam bentuk fungsi. Artinya,
fungsi Jumlah10dan2
memanggil fungsi jumlah untuk mengoperasikan
penjumlahan angka 10 dan 2.
Inner Function
Secara definisi, inner function atau juga dikenal dengan nested function merupakan fungsi yang didefinisikan di dalam fungsi. Pada dasarnya, sebuah fungsi dibuat sebagai inner function untuk memisahkan/membatasi dari semua yang terjadi di luar fungsi induk (outer function). Artinya, inner function hanya bisa mengakses variabel dari lingkup fungsi induk, dan tidak dapat berinteraksi dengan variabel di luar fungsi induk.
def OuterFunc():
print("Ini adalah Outer Function")
def InnerFunc():
print("Ini adalah Inner Function")
return InnerFunc()
OuterFunc()
Walaupun inner function didefinisikan seperti fungsi pada umumnya, fungsi ini tidak bisa dipanggil secara langsung. Hal ini karena fungsi tersebut tidak terdefinisi secara global. Inner Function merupakan object lokal yang hanya akan terdefinisi jika fungsi induknya dipanggil.
Fungsi yang Mengembalikan Fungsi
Selain dapat mengembalikan nilai, fungsi juga dapat suatu mengembalikan
(object) fungsi. Untuk lebih
jelasnya perhatikan contoh berikut.
Pada fungsi berikut, fungsi menu()
menerima argumen berupa huruf. Apabila
pengguna memasukkan huruf a
maka fungsi akan mengembalikan fungsi pizza
.
Proses ini merupakan proses pengembalian fungsi dari fungsi.
def menu(huruf):
def pizza():
return "You ordered Pizza"
def bakso():
return "You ordered Bakso"
if huruf=='a':
return pizza
else:
return bakso
Pada contoh di atas, fungsi pizza dan bakso dikembalikan tanpa tanda kurung (parentheses). Dengan cara ini, pengguna sebenarnya tidak memanggil fungsi melainkan memanggil referensi objek fungsi tersebut:
menu('a')
# referensi ke object fungsi pizza
menu('b')
# referensi ke object fungsi bakso
Untuk mengembalikan nilai berupa “You ordered pizza/bakso”, fungsi harus dipanggil menggunakan parentheses. Oleh karena itu untuk mengembalikan nilai dari fungsi tersebut, pengguna dapat membuat variabel baru yang berisi referensi dari fungsi seperti berikut:
pizza = menu('a')
bakso = menu('b')
pizza()
bakso()
Pada contoh di atas, variabel pizza sudah menyimpan referensi dari fungsi pizza, sehingga variabel ini sekarang telah menjadi fungsi dan dapat dipanggil menggunakan parentheses.
Python Closure
Konsep Python Closure sangat berhubungan dengan inner function dan
variabel non-lokal. Variabel non-lokal adalah variabel yang tidak didefinisikan di dalam
lingkup lokal. Variabel ini digunakan di dalam fungsi bersarang. Berikut ini contoh
penggunaan variabel non-lokal dalam fungsi bernama sapa()
.
def sapa(pesan):
def tampilkan():
print(pesan)
tampilkan()
sapa('Halo Nyayu')
Pada contoh di atas, fungsi tampilkan()
dapat mengakses variabel pesan
yang
terdapat yang terdapat pada fungsi induknya (di luar fungsinya sendiri). Dalam
hal ini, fungsi tampilkan( )
mengakses variabel non-lokal. Sehingga, output fungsi
sapa('Halo Nyayu')
dapat ditampilkan pada layar. Untuk bisa menjalankan fungsi
sapa()
, pengguna harus memberikan argumen wajib pada fungsi tersebut seperti:
sapa('Hello Nyayu')
, sapa('Apa Kabar')
dan sebagainya.
Apabila pengguna tidak memasukkan argumen, maka fungsi tersebut tidak dapat dijalankan alias akan muncul pesan kesalahan. Pertanyaannya adalah apakah program bisa mengingat sebuah argumen yang telah dimasukkan? sehingga fungsi tersebut dapat dipanggil tanpa argumen. Disini peran closure, Python menyediakan cara untuk mengingat nilai dimasukkan pada fungsi dengan cara seperti yang telah kita gunakan pada contoh di bagian inner function: menyimpan referensi fungsi ke dalam sebuah variabel.
def sapa(pesan):
def tampilkan():
print(pesan)
tampilkan()
sapanyayu = sapa('Halo Nyayu')
sapanyayu()
Pada contoh di atas, fungsi sapa()
dengan argumen “Hello Nyayu” disimpan ke
dalam variabel bernama sapanyayu
sehingga sapanyayu
menjadi fungsi. Pada saat
pemanggilan sapanyayu()
, pesan “Hello Nyayu” masih tetap eksis meskipun fungsi
sapa()
telah selesai dijalankan. Padahal pada fungsi biasa, seharusnya argumen
pada fungsi akan terhapus begitu eksekusi terhadap fungsi selesai.
Metode penyimpanan argumen ("Hello Nyayu")
terhadap suatu variabel inilah
yang disebut dengan closure. Pada contoh fungsi di atas, bagian yang menunjukkan
sebagai fungsi closure adalah sebagai berikut:
sapanyayu = sapa('Halo Nyayu')
sapanyayu()
Dengan demikian, jelas bahwa closure adalah sebuah fungsi yang dapat disimpan dalam variabel dan biasa dimanfaatkan untuk mewakili (enclose) sebuah proses pada blok tertentu. Variabel yang menyimpan closure memiliki sifat seperti fungsi yang disimpannya. Dengan menggunakan closure, fungsi dapat mengembalikan objek (atau fungsi lain) dan mengingat lingkungan tempat fungsi tersebut diinisiasi. Nilai yang ada pada outer scope masih diingat meskipun fungsi tersebut telah dihapus. Closure memiliki peran yang sangat penting dalam decorator. Closure digunakan untuk menyimpan nilai fungsi yang telah di dekorasi.
Membuat Decorator Sederhana pada Fungsi Tanpa Parameter
Decorator adalah fungsi yang memodifikasi fungsi lain. Oleh karena itu, decorator merupakan fungsi yang menerima fungsi lain sebagai argumennya (first-class object). Dengan menggunakan decorator, proses mendekorasi fungsi menjadi lebih ringkas dan relatif simpel. Untuk membuat decorator, fungsi untuk decorator tersebut harus terlebih dahulu didefinisikan. Pada contoh kali ini, decorator akan digunakan untuk sebuah fungsi sederhana tanpa parameter.
def inidecorator(fungsinama):
def halo():
print('Halo')
fungsinama()
print('Apa kabar?')
return halo
Pada contoh di atas, inidecorator()
adalah fungsi yang akan dibuat sebagai
decorator. Fungsi di atas akan menerima argumen yang juga merupakan fungsi
dengan input berupa “nama”. Fungsi yang diterima kemudian akan dimodifikasi
dengan menambahkan kata “Halo” dan “Apa kabar?”. Decorator tersebut akan
digunakan untuk mendekorasi fungsi sederhana yang tanpa parameter seperti
berikut ini.
def person():
print('Nyayu')
Fungsi person()
akan didekorasi menggunakan fungsi decorator. Fungsi yang telah
didekorasi disimpan ke dalam variabel yang diberi nama person seperti berikut:
person = decorator(person)
Nama tersebut tidak harus sama dengan nama fungsi yang di dekorasi, kita
dapat memberikan nama apapun sesuai yang diinginkan. Ketika fungsi person()
dipanggil, fungsi ini akan dikembalikan menjadi fungsi yang telah didekorasi.
Artinya, di dalam fungsi person()
sekarang tidak hanya berisi kata “Nyayu”
melainkan sebagai berikut:
person()
# Halo
# Nyayu
# Apa kabar?
Sintaks Umum untuk Decorator pada Python
Python memiliki cara yang lebih sederhana untuk mendekorasi fungsi yaitu dengan
menggunakan simbol @
diikuti dengan nama fungsi decorator tersebut. Fungsi ini
ditempatkan di atas fungsi yang akan didekorasi. Sehingga hasilnya akan menjadi
seperti berikut ini:
@inidecorator
def person():
print('Nyayu')
person()
Fungsi di atas sama dengan fungsi sebelumnya yaitu:
def person():
print('Nyayu')
person = decorator(person)
person()
Membuat Decorator untuk Fungsi dengan Argumen
Pada contoh sebelumnya, decorator yang dibuat sangat sederhana dan hanya berlaku untuk mendekorasi fungsi yang tidak memiliki parameter. Apabila decorator tersebut dipakai untuk fungsi yang memiliki argumen seperti berikut:
def person2(nama):
print(nama)
maka akan menghasilkan pesan error karena inner function (fungsi halo()
) pada
fungsi decorator tidak menerima argumen. Akan tetapi pada saat fungsi dipanggil,
argumen ‘Nyayu’ diberikan kepada inner function.
@inidecorator
def person2(nama):
print(nama)
person('Nyayu')
Supaya decorator @inidecorator
dapat mendekorasi fungsi dengan
argumen, perlu ditambahkan argumen *args
dan **kwargs
di
dalam fungsi halo()
pada decorator.
def inidecorator(fungsinama):
def halo(*args, **kwargs):
print('Halo')
fungsinama(*args,**kwargs)
print('Apa kabar?')
return halo
Penambahan argumen *args
dan **kwargs
menyebabkan fungsi halo()
bisa
menerima sejumlah argumen. Dengan demikian, @inidecorator
bisa mendekorasi
fungsi person2()
yang menerima input berupa argumen nama.
Penulis: Nyayu Fitria Romadhona Editor : AT, EM