Memahami Decorator dalam Python
Bahasa pemrograman Python menyediakan berbagai fitur yang membantu programmer menyusun kode secara terstruktur, ringkas, dan mudah dikembangkan. Salah satu fitur yang memiliki peranan penting dalam pengembangan program adalah *decorator*. Fitur ini memungkinkan programmer menambahkan atau mengubah perilaku suatu fungsi tanpa harus mengubah secara langsung kode utama yang terdapat di dalam fungsi tersebut. Dengan memanfaatkan decorator, berbagai kebutuhan tambahan dapat diterapkan secara teratur tanpa menuliskan instruksi yang sama berulang kali pada setiap fungsi.
Sebelum mempelajari materi tentang cara Memahami Decorator dalam Python, terlebih dahulu pelajari materi tentang: Mengenal Generator dalam Python, Memahami Namespace dan Scope Variabel dalam Python, dan Memahami Python Closures.
Sebelum mempelajari materi tentang cara Memahami Decorator dalam Python, terlebih dahulu pelajari materi tentang: Mengenal Generator dalam Python, Memahami Namespace dan Scope Variabel dalam Python, dan Memahami Python Closures.
Dalam program sederhana, sebuah fungsi biasanya dibuat untuk melaksanakan tugas tertentu, seperti menghitung nilai, mengolah data, menampilkan informasi, atau menghasilkan keluaran berdasarkan masukan. Seiring bertambahnya kompleksitas program, terkadang diperlukan proses tambahan yang berlaku pada sejumlah fungsi sekaligus. Sebagai contoh, programmer mungkin perlu menampilkan pesan sebelum dan sesudah fungsi dijalankan, memeriksa masukan, mencatat aktivitas program, atau memastikan bahwa fungsi hanya dijalankan ketika persyaratan tertentu terpenuhi.
Tanpa mekanisme yang tepat, instruksi tambahan tersebut dapat menyebabkan pengulangan kode. Setiap fungsi harus dilengkapi dengan instruksi yang serupa, sehingga program menjadi lebih panjang dan pemeliharaannya semakin sulit. Decorator memberikan pendekatan yang lebih praktis dengan membungkus fungsi menggunakan fungsi lain yang bertugas menambahkan perilaku tertentu. Fungsi utama tetap dapat menjalankan tanggung jawabnya, sedangkan perilaku tambahan dikelola secara terpisah.
Decorator berkaitan erat dengan beberapa konsep dasar Python, yaitu fungsi sebagai objek, fungsi bertingkat, fungsi yang diteruskan sebagai argumen, fungsi yang dikembalikan sebagai nilai, serta *closure*. Konsep-konsep tersebut menjadi landasan bagi mekanisme decorator. Dengan memahaminya secara bertahap, programmer dapat mengetahui bagaimana decorator bekerja dan mengapa simbol `@` dapat digunakan untuk menerapkannya dengan sintaks yang ringkas.
Pembahasan ini menguraikan pengertian decorator, prinsip kerja, penggunaan simbol `@`, penerapan pada fungsi yang memiliki parameter, penggabungan beberapa decorator, serta contoh penggunaannya dalam berbagai kebutuhan pemrograman. Setiap pembahasan dilengkapi dengan contoh kode dan hasil program agar konsep dapat dipahami secara lebih mendalam.
Sebagai contoh, sebuah fungsi bernama `sapa()` bertugas menampilkan salam. Programmer dapat membuat decorator yang menampilkan pesan tambahan sebelum fungsi tersebut dijalankan. Dengan cara ini, fungsi `sapa()` tetap berisi instruksi untuk menampilkan salam, sedangkan proses tambahan ditempatkan di dalam decorator.
Perhatikan contoh berikut:
def buat_dekorasi(fungsi):
def pembungkus():
print("Program akan dijalankan")
fungsi()
return pembungkus
def sapa():
print("Halo, selamat belajar Python")
fungsi_baru = buat_dekorasi(sapa)
fungsi_baru()
Hasil program:
Program akan dijalankan
Halo, selamat belajar Python
Pada kode tersebut, `buat_dekorasi()` merupakan fungsi decorator. Fungsi tersebut menerima fungsi lain melalui parameter `fungsi`. Di dalamnya terdapat fungsi bertingkat bernama `pembungkus()` yang menampilkan pesan tambahan, kemudian menjalankan fungsi asli.
Pernyataan `return pembungkus` mengembalikan objek fungsi `pembungkus`, bukan hasil pemanggilannya. Objek fungsi tersebut kemudian disimpan dalam variabel `fungsi_baru`. Ketika `fungsi_baru()` dipanggil, pesan tambahan ditampilkan terlebih dahulu, kemudian fungsi `sapa()` dijalankan.
Contoh tersebut menunjukkan bahwa decorator dapat menambahkan perilaku baru tanpa perlu mengubah isi fungsi `sapa()`. Apabila perilaku tambahan perlu diterapkan pada fungsi lain, decorator yang sama dapat digunakan kembali selama struktur pembungkusnya sesuai dengan kebutuhan fungsi tersebut.
Perhatikan contoh berikut:
def tambah(a, b):
return a + b
operasi = tambah
print(operasi(5, 3))
Hasil program:
8
Variabel `operasi` merujuk pada objek fungsi `tambah`. Ketika `operasi(5, 3)` dipanggil, Python menjalankan fungsi yang sama dengan `tambah(5, 3)`. Hal ini menunjukkan bahwa fungsi dapat disimpan dalam variabel tanpa harus menjalankannya terlebih dahulu.
Konsep tersebut penting bagi decorator karena fungsi asli perlu diteruskan kepada decorator sebagai objek. Decorator kemudian dapat membungkus fungsi tersebut dan mengembalikan fungsi baru yang memiliki perilaku tambahan.
Contohnya adalah sebagai berikut:
def tambah(a, b):
return a + b
def hitung(operasi, x, y):
return operasi(x, y)
hasil = hitung(tambah, 4, 6)
print(hasil)
Hasil program:
10
Fungsi `hitung()` menerima tiga argumen, yaitu `operasi`, `x`, dan `y`. Argumen `operasi` diisi dengan objek fungsi `tambah`, sedangkan `x` dan `y` masing-masing bernilai `4` dan `6`. Ketika `operasi(x, y)` dijalankan, Python menjalankan fungsi `tambah(4, 6)`.
Kemampuan menerima fungsi sebagai argumen merupakan salah satu dasar pembentukan decorator. Decorator menerima fungsi yang hendak dihias, kemudian menentukan bagaimana fungsi tersebut akan dijalankan.
Contohnya sebagai berikut:
def buat_salam(nama):
def tampilkan():
return f"Halo, {nama}!"
return tampilkan
salam = buat_salam("Rina")
print(salam())
Hasil program:
Halo, Rina!
Fungsi `buat_salam()` mengembalikan objek fungsi `tampilkan`. Fungsi bagian dalam tersebut tetap dapat menggunakan variabel `nama` yang berasal dari fungsi luar. Hubungan ini merupakan contoh *closure*, yaitu mekanisme yang memungkinkan fungsi mempertahankan akses terhadap variabel dari lingkungan luar yang melingkupinya.
Decorator sering memanfaatkan fungsi bertingkat dan closure untuk mempertahankan akses terhadap fungsi asli serta informasi lain yang diperlukan oleh fungsi pembungkus.
Perhatikan contoh berikut:
def dekorator(fungsi):
def pembungkus():
print("Sebelum fungsi dijalankan")
fungsi()
print("Sesudah fungsi dijalankan")
return pembungkus
def tampilkan_pesan():
print("Ini adalah fungsi utama")
hasil = dekorator(tampilkan_pesan)
hasil()
Hasil program:
Sebelum fungsi dijalankan
Ini adalah fungsi utama
Sesudah fungsi dijalankan
Ketika `dekorator(tampilkan_pesan)` dipanggil, objek fungsi `tampilkan_pesan` diteruskan sebagai argumen kepada decorator. Di dalam decorator, fungsi `pembungkus()` didefinisikan untuk mengatur urutan pelaksanaan instruksi.
Ketika `hasil()` dipanggil, fungsi pembungkus dijalankan. Pertama, pembungkus menampilkan pesan sebelum fungsi utama. Kedua, pembungkus memanggil fungsi asli melalui `fungsi()`. Ketiga, pembungkus menampilkan pesan setelah fungsi asli selesai dijalankan.
Urutan tersebut memperlihatkan bahwa decorator dapat mengatur proses yang berlangsung di sekitar fungsi utama. Perilaku tambahan dapat berupa pencatatan aktivitas, pemeriksaan kondisi, pengukuran waktu, atau pengolahan nilai hasil fungsi.
Perlu diperhatikan bahwa contoh di atas belum mengembalikan nilai dari fungsi asli. Jika fungsi asli menghasilkan nilai melalui `return`, fungsi pembungkus perlu mengembalikan nilai tersebut agar hasil fungsi tidak hilang. Hal ini akan dibahas pada bagian decorator dengan parameter dan nilai balik.
Perhatikan contoh berikut:
def dekorator(fungsi):
def pembungkus():
print("Fungsi telah dihias")
fungsi()
return pembungkus
@dekorator
def tampilkan():
print("Belajar decorator Python")
tampilkan()
Hasil program:
Fungsi telah dihias
Belajar decorator Python
Simbol `@dekorator` yang diletakkan tepat di atas definisi fungsi `tampilkan()` berarti bahwa fungsi tersebut diteruskan kepada decorator dan nama `tampilkan` kemudian merujuk pada fungsi hasil dekorasi.
Secara konseptual, kode berikut:
@dekorator
def tampilkan():
print("Belajar decorator Python")
setara dengan penerapan manual berikut:
def tampilkan():
print("Belajar decorator Python")
tampilkan = dekorator(tampilkan)
Kedua bentuk tersebut menerapkan decorator dengan tujuan yang sama. Penggunaan `@` membuat kode lebih ringkas dan memudahkan pembaca mengetahui bahwa fungsi tertentu memiliki perilaku tambahan.
Simbol `@` bukanlah operator khusus yang hanya digunakan untuk satu jenis fungsi. Dalam konteks decorator, simbol tersebut menjadi bagian dari sintaks yang menghubungkan definisi fungsi dengan decorator yang diterapkan. Nama decorator yang digunakan setelah simbol `@` harus merujuk pada objek yang dapat dipanggil dan memenuhi kebutuhan penerapan tersebut.
Sebagai contoh, sebuah fungsi pembagian membutuhkan dua argumen. Programmer dapat membuat decorator untuk memeriksa apakah penyebut bernilai nol sebelum operasi pembagian dilakukan.
def cek_pembagian(fungsi):
def pembungkus(a, b):
print("Memeriksa operasi pembagian")
if b == 0:
print("Pembagian dengan nol tidak diperbolehkan")
return None
return fungsi(a, b)
return pembungkus
@cek_pembagian
def bagi(a, b):
return a / b
print(bagi(10, 2))
print(bagi(10, 0))
Hasil program:
Memeriksa operasi pembagian
5.0
Memeriksa operasi pembagian
Pembagian dengan nol tidak diperbolehkan
None
Pada contoh tersebut, decorator `cek_pembagian()` menerima fungsi `bagi()`. Fungsi pembungkus bernama `pembungkus()` menerima dua parameter, yaitu `a` dan `b`. Sebelum fungsi asli dijalankan, pembungkus memeriksa apakah nilai `b` sama dengan nol.
Jika `b` bernilai nol, pembungkus menampilkan pesan dan mengembalikan `None`. Jika penyebut tidak nol, pembungkus menjalankan fungsi asli dengan memanggil `fungsi(a, b)` dan mengembalikan hasilnya melalui `return`.
Contoh tersebut memperlihatkan bahwa decorator dapat digunakan untuk menambahkan pemeriksaan terhadap kondisi tertentu. Namun, pembungkus yang hanya menerima dua parameter belum tentu cocok untuk semua fungsi. Jika decorator perlu digunakan pada fungsi dengan jumlah atau jenis parameter yang berbeda-beda, pendekatan yang lebih fleksibel dapat diterapkan.
Perhatikan contoh berikut:
def catat_pemanggilan(fungsi):
def pembungkus(*args, **kwargs):
print("Fungsi mulai dijalankan")
hasil = fungsi(*args, **kwargs)
print("Fungsi selesai dijalankan")
return hasil
return pembungkus
@catat_pemanggilan
def tambah(a, b):
return a + b
@catat_pemanggilan
def sapa(nama, sapaan="Halo"):
return f"{sapaan}, {nama}!"
print("Hasil:", tambah(4, 7))
print(sapa("Dina", sapaan="Selamat pagi"))
Hasil program:
Fungsi mulai dijalankan
Fungsi selesai dijalankan
Hasil: 11
Fungsi mulai dijalankan
Fungsi selesai dijalankan
Selamat pagi, Dina!
Fungsi pembungkus menggunakan `*args` dan `**kwargs` agar dapat menerima argumen posisional maupun argumen kata kunci. Argumen tersebut kemudian diteruskan kepada fungsi asli melalui `fungsi(*args, **kwargs)`.
Variabel `hasil` digunakan untuk menyimpan nilai yang dikembalikan oleh fungsi asli. Setelah pesan penutup ditampilkan, pembungkus mengembalikan nilai tersebut melalui `return hasil`. Dengan demikian, decorator tidak menghilangkan hasil yang seharusnya diterima oleh pemanggil fungsi.
Pendekatan ini lebih fleksibel dibandingkan pembungkus yang hanya menerima parameter tertentu. Meskipun demikian, `*args` dan `**kwargs` tidak otomatis menjamin semua perilaku fungsi asli tetap terjaga. Untuk membuat decorator yang lebih baik, programmer juga perlu memperhatikan informasi fungsi, seperti nama dan dokumentasinya.
Python menyediakan `functools.wraps` untuk membantu mempertahankan metadata fungsi asli pada fungsi pembungkus. Penggunaan fitur ini merupakan praktik yang dianjurkan ketika membuat decorator untuk digunakan dalam program yang lebih besar.
Perhatikan contoh berikut:
from functools import wraps
def dekorator(fungsi):
@wraps(fungsi)
def pembungkus(*args, **kwargs):
print("Menjalankan fungsi")
return fungsi(*args, **kwargs)
return pembungkus
@dekorator
def hitung_luas(panjang, lebar):
"""Menghitung luas persegi panjang."""
return panjang * lebar
print(hitung_luas(8, 5))
print(hitung_luas.__name__)
print(hitung_luas.__doc__)
Hasil program:
Menjalankan fungsi
40
hitung_luas
Menghitung luas persegi panjang.
Pada contoh tersebut, `@wraps(fungsi)` digunakan di dalam decorator untuk membantu mempertahankan metadata fungsi asli. Akibatnya, nama fungsi yang terlihat melalui `hitung_luas.__name__` tetap `hitung_luas`, sedangkan dokumentasinya tetap dapat diakses melalui `hitung_luas.__doc__`.
Metadata tersebut berguna dalam proses pemeriksaan program, pembuatan dokumentasi, pencatatan aktivitas, dan penggunaan pustaka yang memerlukan informasi mengenai fungsi. Tanpa `wraps`, fungsi pembungkus tetap dapat bekerja, tetapi informasi yang terlihat melalui sejumlah atribut fungsi dapat menjadi kurang sesuai dengan fungsi asli.
Penggunaan `functools.wraps` tidak mengubah tujuan utama decorator. Fitur ini melengkapi decorator agar perilaku tambahan dapat diterapkan tanpa kehilangan informasi penting yang melekat pada fungsi yang dibungkus.
Perhatikan contoh berikut:
def bintang(fungsi):
def pembungkus(*args, **kwargs):
print("*" * 15)
hasil = fungsi(*args, **kwargs)
print("*" * 15)
return hasil
return pembungkus
def persen(fungsi):
def pembungkus(*args, **kwargs):
print("%" * 15)
hasil = fungsi(*args, **kwargs)
print("%" * 15)
return hasil
return pembungkus
@bintang
@persen
def tampilkan_pesan(pesan):
print(pesan)
tampilkan_pesan("Halo, Python!")
Tanpa mekanisme yang tepat, instruksi tambahan tersebut dapat menyebabkan pengulangan kode. Setiap fungsi harus dilengkapi dengan instruksi yang serupa, sehingga program menjadi lebih panjang dan pemeliharaannya semakin sulit. Decorator memberikan pendekatan yang lebih praktis dengan membungkus fungsi menggunakan fungsi lain yang bertugas menambahkan perilaku tertentu. Fungsi utama tetap dapat menjalankan tanggung jawabnya, sedangkan perilaku tambahan dikelola secara terpisah.
Decorator berkaitan erat dengan beberapa konsep dasar Python, yaitu fungsi sebagai objek, fungsi bertingkat, fungsi yang diteruskan sebagai argumen, fungsi yang dikembalikan sebagai nilai, serta *closure*. Konsep-konsep tersebut menjadi landasan bagi mekanisme decorator. Dengan memahaminya secara bertahap, programmer dapat mengetahui bagaimana decorator bekerja dan mengapa simbol `@` dapat digunakan untuk menerapkannya dengan sintaks yang ringkas.
Pembahasan ini menguraikan pengertian decorator, prinsip kerja, penggunaan simbol `@`, penerapan pada fungsi yang memiliki parameter, penggabungan beberapa decorator, serta contoh penggunaannya dalam berbagai kebutuhan pemrograman. Setiap pembahasan dilengkapi dengan contoh kode dan hasil program agar konsep dapat dipahami secara lebih mendalam.
Pengertian Decorator dalam Python
Decorator adalah fungsi atau objek yang dapat dipanggil yang menerima suatu fungsi atau objek lain, kemudian mengembalikan fungsi atau objek dengan perilaku yang telah ditambahkan atau diubah. Dalam penggunaan yang paling umum, decorator digunakan untuk membungkus fungsi dengan fungsi lain sehingga perilaku tambahan dapat dijalankan sebelum, sesudah, atau di sekitar pelaksanaan fungsi asli.Sebagai contoh, sebuah fungsi bernama `sapa()` bertugas menampilkan salam. Programmer dapat membuat decorator yang menampilkan pesan tambahan sebelum fungsi tersebut dijalankan. Dengan cara ini, fungsi `sapa()` tetap berisi instruksi untuk menampilkan salam, sedangkan proses tambahan ditempatkan di dalam decorator.
Perhatikan contoh berikut:
def buat_dekorasi(fungsi):
def pembungkus():
print("Program akan dijalankan")
fungsi()
return pembungkus
def sapa():
print("Halo, selamat belajar Python")
fungsi_baru = buat_dekorasi(sapa)
fungsi_baru()
Hasil program:
Program akan dijalankan
Halo, selamat belajar Python
Pada kode tersebut, `buat_dekorasi()` merupakan fungsi decorator. Fungsi tersebut menerima fungsi lain melalui parameter `fungsi`. Di dalamnya terdapat fungsi bertingkat bernama `pembungkus()` yang menampilkan pesan tambahan, kemudian menjalankan fungsi asli.
Pernyataan `return pembungkus` mengembalikan objek fungsi `pembungkus`, bukan hasil pemanggilannya. Objek fungsi tersebut kemudian disimpan dalam variabel `fungsi_baru`. Ketika `fungsi_baru()` dipanggil, pesan tambahan ditampilkan terlebih dahulu, kemudian fungsi `sapa()` dijalankan.
Contoh tersebut menunjukkan bahwa decorator dapat menambahkan perilaku baru tanpa perlu mengubah isi fungsi `sapa()`. Apabila perilaku tambahan perlu diterapkan pada fungsi lain, decorator yang sama dapat digunakan kembali selama struktur pembungkusnya sesuai dengan kebutuhan fungsi tersebut.
Konsep Dasar yang Mendukung Decorator
Sebelum mempelajari penggunaan decorator lebih lanjut, terdapat beberapa konsep Python yang perlu dipahami. Konsep-konsep tersebut menjelaskan mengapa suatu fungsi dapat menerima fungsi lain, mengembalikan fungsi, dan mempertahankan akses terhadap variabel yang berasal dari lingkungan luar.Fungsi sebagai Objek
Dalam Python, fungsi merupakan objek. Artinya, fungsi dapat diperlakukan seperti objek lainnya. Fungsi dapat disimpan dalam variabel, diteruskan sebagai argumen, dikembalikan oleh fungsi lain, dan digunakan kembali melalui nama yang merujuk pada objek tersebut.Perhatikan contoh berikut:
def tambah(a, b):
return a + b
operasi = tambah
print(operasi(5, 3))
Hasil program:
8
Variabel `operasi` merujuk pada objek fungsi `tambah`. Ketika `operasi(5, 3)` dipanggil, Python menjalankan fungsi yang sama dengan `tambah(5, 3)`. Hal ini menunjukkan bahwa fungsi dapat disimpan dalam variabel tanpa harus menjalankannya terlebih dahulu.
Konsep tersebut penting bagi decorator karena fungsi asli perlu diteruskan kepada decorator sebagai objek. Decorator kemudian dapat membungkus fungsi tersebut dan mengembalikan fungsi baru yang memiliki perilaku tambahan.
Fungsi sebagai Argumen
Python memungkinkan sebuah fungsi menerima fungsi lain sebagai argumen. Pendekatan ini sering digunakan ketika suatu fungsi perlu menjalankan operasi yang ditentukan oleh fungsi lain.Contohnya adalah sebagai berikut:
def tambah(a, b):
return a + b
def hitung(operasi, x, y):
return operasi(x, y)
hasil = hitung(tambah, 4, 6)
print(hasil)
Hasil program:
10
Fungsi `hitung()` menerima tiga argumen, yaitu `operasi`, `x`, dan `y`. Argumen `operasi` diisi dengan objek fungsi `tambah`, sedangkan `x` dan `y` masing-masing bernilai `4` dan `6`. Ketika `operasi(x, y)` dijalankan, Python menjalankan fungsi `tambah(4, 6)`.
Kemampuan menerima fungsi sebagai argumen merupakan salah satu dasar pembentukan decorator. Decorator menerima fungsi yang hendak dihias, kemudian menentukan bagaimana fungsi tersebut akan dijalankan.
Mengembalikan Fungsi sebagai Nilai
Selain menerima fungsi sebagai argumen, Python memungkinkan sebuah fungsi mengembalikan fungsi lain. Kemampuan ini menjadi bagian penting dalam pembuatan decorator karena decorator umumnya mengembalikan fungsi pembungkus.Contohnya sebagai berikut:
def buat_salam(nama):
def tampilkan():
return f"Halo, {nama}!"
return tampilkan
salam = buat_salam("Rina")
print(salam())
Hasil program:
Halo, Rina!
Fungsi `buat_salam()` mengembalikan objek fungsi `tampilkan`. Fungsi bagian dalam tersebut tetap dapat menggunakan variabel `nama` yang berasal dari fungsi luar. Hubungan ini merupakan contoh *closure*, yaitu mekanisme yang memungkinkan fungsi mempertahankan akses terhadap variabel dari lingkungan luar yang melingkupinya.
Decorator sering memanfaatkan fungsi bertingkat dan closure untuk mempertahankan akses terhadap fungsi asli serta informasi lain yang diperlukan oleh fungsi pembungkus.
Cara Kerja Decorator
Cara kerja decorator dapat dipahami melalui hubungan antara fungsi asli, fungsi decorator, dan fungsi pembungkus. Fungsi asli berisi tugas utama, decorator menerima fungsi asli sebagai argumen, sedangkan fungsi pembungkus mengatur pelaksanaan perilaku tambahan.Perhatikan contoh berikut:
def dekorator(fungsi):
def pembungkus():
print("Sebelum fungsi dijalankan")
fungsi()
print("Sesudah fungsi dijalankan")
return pembungkus
def tampilkan_pesan():
print("Ini adalah fungsi utama")
hasil = dekorator(tampilkan_pesan)
hasil()
Hasil program:
Sebelum fungsi dijalankan
Ini adalah fungsi utama
Sesudah fungsi dijalankan
Ketika `dekorator(tampilkan_pesan)` dipanggil, objek fungsi `tampilkan_pesan` diteruskan sebagai argumen kepada decorator. Di dalam decorator, fungsi `pembungkus()` didefinisikan untuk mengatur urutan pelaksanaan instruksi.
Ketika `hasil()` dipanggil, fungsi pembungkus dijalankan. Pertama, pembungkus menampilkan pesan sebelum fungsi utama. Kedua, pembungkus memanggil fungsi asli melalui `fungsi()`. Ketiga, pembungkus menampilkan pesan setelah fungsi asli selesai dijalankan.
Urutan tersebut memperlihatkan bahwa decorator dapat mengatur proses yang berlangsung di sekitar fungsi utama. Perilaku tambahan dapat berupa pencatatan aktivitas, pemeriksaan kondisi, pengukuran waktu, atau pengolahan nilai hasil fungsi.
Perlu diperhatikan bahwa contoh di atas belum mengembalikan nilai dari fungsi asli. Jika fungsi asli menghasilkan nilai melalui `return`, fungsi pembungkus perlu mengembalikan nilai tersebut agar hasil fungsi tidak hilang. Hal ini akan dibahas pada bagian decorator dengan parameter dan nilai balik.
Penggunaan Simbol `@` pada Decorator
Python menyediakan simbol `@` sebagai sintaks ringkas untuk menerapkan decorator. Dengan sintaks ini, programmer tidak perlu secara manual memanggil decorator dan menyimpan hasilnya dalam variabel baru.Perhatikan contoh berikut:
def dekorator(fungsi):
def pembungkus():
print("Fungsi telah dihias")
fungsi()
return pembungkus
@dekorator
def tampilkan():
print("Belajar decorator Python")
tampilkan()
Hasil program:
Fungsi telah dihias
Belajar decorator Python
Simbol `@dekorator` yang diletakkan tepat di atas definisi fungsi `tampilkan()` berarti bahwa fungsi tersebut diteruskan kepada decorator dan nama `tampilkan` kemudian merujuk pada fungsi hasil dekorasi.
Secara konseptual, kode berikut:
@dekorator
def tampilkan():
print("Belajar decorator Python")
setara dengan penerapan manual berikut:
def tampilkan():
print("Belajar decorator Python")
tampilkan = dekorator(tampilkan)
Kedua bentuk tersebut menerapkan decorator dengan tujuan yang sama. Penggunaan `@` membuat kode lebih ringkas dan memudahkan pembaca mengetahui bahwa fungsi tertentu memiliki perilaku tambahan.
Simbol `@` bukanlah operator khusus yang hanya digunakan untuk satu jenis fungsi. Dalam konteks decorator, simbol tersebut menjadi bagian dari sintaks yang menghubungkan definisi fungsi dengan decorator yang diterapkan. Nama decorator yang digunakan setelah simbol `@` harus merujuk pada objek yang dapat dipanggil dan memenuhi kebutuhan penerapan tersebut.
Decorator untuk Fungsi yang Memiliki Parameter
Decorator sederhana yang telah dibahas sebelumnya hanya dapat digunakan untuk fungsi tanpa parameter karena fungsi pembungkusnya didefinisikan tanpa argumen. Jika fungsi asli menerima parameter, pembungkus harus dirancang agar dapat menerima dan meneruskan parameter tersebut.Sebagai contoh, sebuah fungsi pembagian membutuhkan dua argumen. Programmer dapat membuat decorator untuk memeriksa apakah penyebut bernilai nol sebelum operasi pembagian dilakukan.
def cek_pembagian(fungsi):
def pembungkus(a, b):
print("Memeriksa operasi pembagian")
if b == 0:
print("Pembagian dengan nol tidak diperbolehkan")
return None
return fungsi(a, b)
return pembungkus
@cek_pembagian
def bagi(a, b):
return a / b
print(bagi(10, 2))
print(bagi(10, 0))
Hasil program:
Memeriksa operasi pembagian
5.0
Memeriksa operasi pembagian
Pembagian dengan nol tidak diperbolehkan
None
Pada contoh tersebut, decorator `cek_pembagian()` menerima fungsi `bagi()`. Fungsi pembungkus bernama `pembungkus()` menerima dua parameter, yaitu `a` dan `b`. Sebelum fungsi asli dijalankan, pembungkus memeriksa apakah nilai `b` sama dengan nol.
Jika `b` bernilai nol, pembungkus menampilkan pesan dan mengembalikan `None`. Jika penyebut tidak nol, pembungkus menjalankan fungsi asli dengan memanggil `fungsi(a, b)` dan mengembalikan hasilnya melalui `return`.
Contoh tersebut memperlihatkan bahwa decorator dapat digunakan untuk menambahkan pemeriksaan terhadap kondisi tertentu. Namun, pembungkus yang hanya menerima dua parameter belum tentu cocok untuk semua fungsi. Jika decorator perlu digunakan pada fungsi dengan jumlah atau jenis parameter yang berbeda-beda, pendekatan yang lebih fleksibel dapat diterapkan.
Penggunaan `*args` dan `**kwargs`
Dalam Python, `*args` digunakan untuk menerima sejumlah argumen posisional, sedangkan `**kwargs` digunakan untuk menerima argumen kata kunci. Kedua fitur tersebut membantu membuat fungsi pembungkus yang dapat digunakan pada berbagai fungsi dengan susunan parameter berbeda.Perhatikan contoh berikut:
def catat_pemanggilan(fungsi):
def pembungkus(*args, **kwargs):
print("Fungsi mulai dijalankan")
hasil = fungsi(*args, **kwargs)
print("Fungsi selesai dijalankan")
return hasil
return pembungkus
@catat_pemanggilan
def tambah(a, b):
return a + b
@catat_pemanggilan
def sapa(nama, sapaan="Halo"):
return f"{sapaan}, {nama}!"
print("Hasil:", tambah(4, 7))
print(sapa("Dina", sapaan="Selamat pagi"))
Hasil program:
Fungsi mulai dijalankan
Fungsi selesai dijalankan
Hasil: 11
Fungsi mulai dijalankan
Fungsi selesai dijalankan
Selamat pagi, Dina!
Fungsi pembungkus menggunakan `*args` dan `**kwargs` agar dapat menerima argumen posisional maupun argumen kata kunci. Argumen tersebut kemudian diteruskan kepada fungsi asli melalui `fungsi(*args, **kwargs)`.
Variabel `hasil` digunakan untuk menyimpan nilai yang dikembalikan oleh fungsi asli. Setelah pesan penutup ditampilkan, pembungkus mengembalikan nilai tersebut melalui `return hasil`. Dengan demikian, decorator tidak menghilangkan hasil yang seharusnya diterima oleh pemanggil fungsi.
Pendekatan ini lebih fleksibel dibandingkan pembungkus yang hanya menerima parameter tertentu. Meskipun demikian, `*args` dan `**kwargs` tidak otomatis menjamin semua perilaku fungsi asli tetap terjaga. Untuk membuat decorator yang lebih baik, programmer juga perlu memperhatikan informasi fungsi, seperti nama dan dokumentasinya.
Mempertahankan Informasi Fungsi dengan `functools.wraps`
Ketika decorator mengganti nama fungsi dengan fungsi pembungkus, beberapa informasi yang sebelumnya melekat pada fungsi asli dapat terlihat berubah. Sebagai contoh, atribut `__name__` pada fungsi yang telah dihias biasanya menunjukkan nama fungsi pembungkus, bukan nama fungsi asli.Python menyediakan `functools.wraps` untuk membantu mempertahankan metadata fungsi asli pada fungsi pembungkus. Penggunaan fitur ini merupakan praktik yang dianjurkan ketika membuat decorator untuk digunakan dalam program yang lebih besar.
Perhatikan contoh berikut:
from functools import wraps
def dekorator(fungsi):
@wraps(fungsi)
def pembungkus(*args, **kwargs):
print("Menjalankan fungsi")
return fungsi(*args, **kwargs)
return pembungkus
@dekorator
def hitung_luas(panjang, lebar):
"""Menghitung luas persegi panjang."""
return panjang * lebar
print(hitung_luas(8, 5))
print(hitung_luas.__name__)
print(hitung_luas.__doc__)
Hasil program:
Menjalankan fungsi
40
hitung_luas
Menghitung luas persegi panjang.
Pada contoh tersebut, `@wraps(fungsi)` digunakan di dalam decorator untuk membantu mempertahankan metadata fungsi asli. Akibatnya, nama fungsi yang terlihat melalui `hitung_luas.__name__` tetap `hitung_luas`, sedangkan dokumentasinya tetap dapat diakses melalui `hitung_luas.__doc__`.
Metadata tersebut berguna dalam proses pemeriksaan program, pembuatan dokumentasi, pencatatan aktivitas, dan penggunaan pustaka yang memerlukan informasi mengenai fungsi. Tanpa `wraps`, fungsi pembungkus tetap dapat bekerja, tetapi informasi yang terlihat melalui sejumlah atribut fungsi dapat menjadi kurang sesuai dengan fungsi asli.
Penggunaan `functools.wraps` tidak mengubah tujuan utama decorator. Fitur ini melengkapi decorator agar perilaku tambahan dapat diterapkan tanpa kehilangan informasi penting yang melekat pada fungsi yang dibungkus.
Menggabungkan Beberapa Decorator
Python memungkinkan lebih dari satu decorator diterapkan pada sebuah fungsi. Teknik ini sering disebut *chaining decorators* atau penggabungan decorator. Setiap decorator dapat menambahkan perilaku tersendiri, sehingga satu fungsi dapat memiliki beberapa lapisan perilaku tambahan.Perhatikan contoh berikut:
def bintang(fungsi):
def pembungkus(*args, **kwargs):
print("*" * 15)
hasil = fungsi(*args, **kwargs)
print("*" * 15)
return hasil
return pembungkus
def persen(fungsi):
def pembungkus(*args, **kwargs):
print("%" * 15)
hasil = fungsi(*args, **kwargs)
print("%" * 15)
return hasil
return pembungkus
@bintang
@persen
def tampilkan_pesan(pesan):
print(pesan)
tampilkan_pesan("Halo, Python!")
Hasil program:
***************
%%%%%%%%%%%%%%%
Halo, Python!
%%%%%%%%%%%%%%%
***************
Pada contoh tersebut, decorator `bintang` dan `persen` diterapkan pada fungsi yang sama. Decorator yang posisinya paling dekat dengan definisi fungsi, yaitu `persen`, diterapkan terlebih dahulu. Hasilnya kemudian dibungkus oleh decorator `bintang`.
Urutan penerapan tersebut secara konseptual setara dengan:
tampilkan_pesan = bintang(persen(tampilkan_pesan))
Ketika fungsi dijalankan, pembungkus `bintang` menampilkan tanda bintang terlebih dahulu. Pembungkus tersebut kemudian memanggil lapisan `persen`, yang menampilkan tanda persen sebelum menjalankan fungsi asli. Setelah fungsi asli selesai, tanda persen ditampilkan kembali, kemudian lapisan terluar menampilkan tanda bintang penutup.
Urutan decorator berpengaruh terhadap hasil akhir. Jika posisi `@bintang` dan `@persen` ditukar, susunan pesan pembuka dan penutup juga berubah. Oleh karena itu, programmer perlu memperhatikan urutan decorator ketika menggabungkan beberapa perilaku pada satu fungsi.
Contoh berikut menunjukkan penerapannya:
def ulangi(jumlah):
def dekorator(fungsi):
def pembungkus(*args, **kwargs):
hasil = None
for _ in range(jumlah):
hasil = fungsi(*args, **kwargs)
return hasil
return pembungkus
return dekorator
@ulangi(3)
def sapa(nama):
print("Halo,", nama)
sapa("Rina")
Hasil program:
Halo, Rina
Halo, Rina
Halo, Rina
Pada contoh tersebut, `ulangi()` merupakan fungsi pembentuk decorator. Fungsi ini menerima parameter `jumlah`, kemudian mengembalikan fungsi `dekorator`. Fungsi `dekorator` menerima fungsi asli dan mengembalikan fungsi pembungkus. Sementara itu, fungsi pembungkus menjalankan fungsi asli sebanyak nilai `jumlah`.
Ketika `@ulangi(3)` digunakan, Python terlebih dahulu memanggil `ulangi(3)` untuk menghasilkan decorator. Decorator tersebut kemudian diterapkan pada fungsi `sapa()`. Saat `sapa("Rina")` dipanggil, fungsi pembungkus menjalankan fungsi asli sebanyak tiga kali.
Contoh ini memiliki tiga tingkat fungsi, yaitu fungsi pembentuk decorator, decorator, dan fungsi pembungkus. Struktur tersebut diperlukan karena argumen `3` bukan argumen langsung untuk fungsi `sapa()`, melainkan konfigurasi yang menentukan bagaimana decorator bekerja.
Penggunaan decorator berargumen bermanfaat ketika perilaku tambahan perlu dikonfigurasi. Nilai pengulangan, tingkat pembatasan, pesan tertentu, atau parameter lain dapat diberikan saat decorator diterapkan. Dengan cara ini, satu pola decorator dapat digunakan untuk beberapa fungsi dengan konfigurasi berbeda.
Contoh berikut menggunakan modul `time` untuk mengukur durasi eksekusi:
import time
from functools import wraps
def ukur_waktu(fungsi):
@wraps(fungsi)
def pembungkus(*args, **kwargs):
mulai = time.perf_counter()
hasil = fungsi(*args, **kwargs)
selesai = time.perf_counter()
durasi = selesai - mulai
print(f"Waktu eksekusi: {durasi:.6f} detik")
return hasil
return pembungkus
@ukur_waktu
def hitung_jumlah():
total = sum(range(1, 100001))
return total
print("Total:", hitung_jumlah())
Contoh keluaran:
Waktu eksekusi: 0.00xxxx detik
Total: 5000050000
Nilai durasi yang sebenarnya bergantung pada perangkat dan kondisi saat program dijalankan. Oleh karena itu, angka waktu pada keluaran dapat berbeda antara satu komputer dan komputer lainnya.
Decorator `ukur_waktu()` mencatat waktu sebelum fungsi asli dijalankan menggunakan `time.perf_counter()`. Setelah fungsi selesai, waktu kembali dicatat. Selisih kedua waktu tersebut menjadi perkiraan durasi pelaksanaan fungsi.
Fungsi pembungkus juga mengembalikan nilai hasil dari fungsi asli. Dengan demikian, fungsi `hitung_jumlah()` tetap menghasilkan jumlah bilangan yang dihitung, sementara informasi durasi ditampilkan sebagai perilaku tambahan.
Pendekatan ini dapat digunakan untuk mengukur berbagai fungsi tanpa menambahkan kode pengukuran waktu secara berulang pada setiap fungsi. Namun, pengukuran sederhana tersebut mencakup pelaksanaan fungsi beserta aktivitas yang terjadi di dalam batas pengukuran, dan hasilnya dapat dipengaruhi oleh kondisi sistem.
Contohnya adalah fungsi yang menghitung luas persegi panjang. Panjang dan lebar tidak seharusnya bernilai negatif.
***************
%%%%%%%%%%%%%%%
Halo, Python!
%%%%%%%%%%%%%%%
***************
Pada contoh tersebut, decorator `bintang` dan `persen` diterapkan pada fungsi yang sama. Decorator yang posisinya paling dekat dengan definisi fungsi, yaitu `persen`, diterapkan terlebih dahulu. Hasilnya kemudian dibungkus oleh decorator `bintang`.
Urutan penerapan tersebut secara konseptual setara dengan:
tampilkan_pesan = bintang(persen(tampilkan_pesan))
Ketika fungsi dijalankan, pembungkus `bintang` menampilkan tanda bintang terlebih dahulu. Pembungkus tersebut kemudian memanggil lapisan `persen`, yang menampilkan tanda persen sebelum menjalankan fungsi asli. Setelah fungsi asli selesai, tanda persen ditampilkan kembali, kemudian lapisan terluar menampilkan tanda bintang penutup.
Urutan decorator berpengaruh terhadap hasil akhir. Jika posisi `@bintang` dan `@persen` ditukar, susunan pesan pembuka dan penutup juga berubah. Oleh karena itu, programmer perlu memperhatikan urutan decorator ketika menggabungkan beberapa perilaku pada satu fungsi.
Decorator yang Menerima Argumen
Decorator juga dapat dirancang agar menerima argumen yang menentukan perilaku tambahan. Misalnya, programmer ingin membuat decorator yang dapat mengulang pelaksanaan suatu fungsi sebanyak jumlah tertentu. Untuk kebutuhan tersebut, diperlukan fungsi pembentuk decorator yang menerima jumlah pengulangan.Contoh berikut menunjukkan penerapannya:
def ulangi(jumlah):
def dekorator(fungsi):
def pembungkus(*args, **kwargs):
hasil = None
for _ in range(jumlah):
hasil = fungsi(*args, **kwargs)
return hasil
return pembungkus
return dekorator
@ulangi(3)
def sapa(nama):
print("Halo,", nama)
sapa("Rina")
Hasil program:
Halo, Rina
Halo, Rina
Halo, Rina
Pada contoh tersebut, `ulangi()` merupakan fungsi pembentuk decorator. Fungsi ini menerima parameter `jumlah`, kemudian mengembalikan fungsi `dekorator`. Fungsi `dekorator` menerima fungsi asli dan mengembalikan fungsi pembungkus. Sementara itu, fungsi pembungkus menjalankan fungsi asli sebanyak nilai `jumlah`.
Ketika `@ulangi(3)` digunakan, Python terlebih dahulu memanggil `ulangi(3)` untuk menghasilkan decorator. Decorator tersebut kemudian diterapkan pada fungsi `sapa()`. Saat `sapa("Rina")` dipanggil, fungsi pembungkus menjalankan fungsi asli sebanyak tiga kali.
Contoh ini memiliki tiga tingkat fungsi, yaitu fungsi pembentuk decorator, decorator, dan fungsi pembungkus. Struktur tersebut diperlukan karena argumen `3` bukan argumen langsung untuk fungsi `sapa()`, melainkan konfigurasi yang menentukan bagaimana decorator bekerja.
Penggunaan decorator berargumen bermanfaat ketika perilaku tambahan perlu dikonfigurasi. Nilai pengulangan, tingkat pembatasan, pesan tertentu, atau parameter lain dapat diberikan saat decorator diterapkan. Dengan cara ini, satu pola decorator dapat digunakan untuk beberapa fungsi dengan konfigurasi berbeda.
Contoh Penerapan Decorator untuk Mengukur Waktu Eksekusi
Salah satu penerapan decorator yang umum adalah mengukur waktu pelaksanaan fungsi. Teknik ini membantu programmer mengetahui berapa lama suatu proses berjalan, misalnya ketika menguji efisiensi algoritma atau membandingkan dua pendekatan pengolahan data.Contoh berikut menggunakan modul `time` untuk mengukur durasi eksekusi:
import time
from functools import wraps
def ukur_waktu(fungsi):
@wraps(fungsi)
def pembungkus(*args, **kwargs):
mulai = time.perf_counter()
hasil = fungsi(*args, **kwargs)
selesai = time.perf_counter()
durasi = selesai - mulai
print(f"Waktu eksekusi: {durasi:.6f} detik")
return hasil
return pembungkus
@ukur_waktu
def hitung_jumlah():
total = sum(range(1, 100001))
return total
print("Total:", hitung_jumlah())
Contoh keluaran:
Waktu eksekusi: 0.00xxxx detik
Total: 5000050000
Nilai durasi yang sebenarnya bergantung pada perangkat dan kondisi saat program dijalankan. Oleh karena itu, angka waktu pada keluaran dapat berbeda antara satu komputer dan komputer lainnya.
Decorator `ukur_waktu()` mencatat waktu sebelum fungsi asli dijalankan menggunakan `time.perf_counter()`. Setelah fungsi selesai, waktu kembali dicatat. Selisih kedua waktu tersebut menjadi perkiraan durasi pelaksanaan fungsi.
Fungsi pembungkus juga mengembalikan nilai hasil dari fungsi asli. Dengan demikian, fungsi `hitung_jumlah()` tetap menghasilkan jumlah bilangan yang dihitung, sementara informasi durasi ditampilkan sebagai perilaku tambahan.
Pendekatan ini dapat digunakan untuk mengukur berbagai fungsi tanpa menambahkan kode pengukuran waktu secara berulang pada setiap fungsi. Namun, pengukuran sederhana tersebut mencakup pelaksanaan fungsi beserta aktivitas yang terjadi di dalam batas pengukuran, dan hasilnya dapat dipengaruhi oleh kondisi sistem.
Contoh Penerapan Decorator untuk Validasi Masukan
Decorator dapat digunakan untuk memeriksa masukan sebelum suatu fungsi menjalankan proses utama. Validasi ini membantu memastikan bahwa data yang diterima sesuai dengan persyaratan tertentu.Contohnya adalah fungsi yang menghitung luas persegi panjang. Panjang dan lebar tidak seharusnya bernilai negatif.
Programmer dapat membuat decorator untuk memeriksa nilai tersebut sebelum perhitungan dilakukan:
from functools import wraps
def validasi_positif(fungsi):
@wraps(fungsi)
def pembungkus(panjang, lebar):
if panjang <= 0 or lebar <= 0:
raise ValueError(
"Panjang dan lebar harus lebih besar dari nol"
)
return fungsi(panjang, lebar)
return pembungkus
@validasi_positif
def luas_persegi_panjang(panjang, lebar):
return panjang * lebar
print(luas_persegi_panjang(8, 5))
Hasil program:
40
Decorator `validasi_positif()` memeriksa kedua parameter sebelum fungsi utama dijalankan. Jika salah satu nilai tidak memenuhi syarat, program menghasilkan `ValueError`. Jika seluruh nilai memenuhi persyaratan, fungsi asli dijalankan dan hasilnya dikembalikan.
Penggunaan `raise ValueError` membuat kesalahan masukan dinyatakan secara eksplisit. Pendekatan ini umumnya lebih tepat daripada hanya menampilkan pesan lalu melanjutkan program seolah-olah operasi berhasil.
Dalam aplikasi nyata, validasi dapat mencakup pemeriksaan jenis data, rentang angka, format teks, atau persyaratan lain. Namun, decorator validasi harus dirancang sesuai dengan tanda tangan fungsi dan aturan data yang diterapkan. Jika fungsi memiliki bentuk parameter berbeda-beda, pembungkus dapat dibuat lebih fleksibel dengan menggunakan `*args` dan `**kwargs`.
Kelebihan berikutnya adalah memisahkan tanggung jawab. Fungsi utama dapat berfokus pada tugas pokoknya, sedangkan decorator menangani proses tambahan seperti pencatatan aktivitas, validasi, pengukuran waktu, atau pemeriksaan akses. Pemisahan tersebut dapat membuat struktur program lebih teratur.
Decorator juga mendukung penggunaan kembali kode. Satu decorator dapat diterapkan pada berbagai fungsi yang sesuai, sehingga programmer tidak perlu menyalin instruksi tambahan pada setiap bagian program. Selain itu, sintaks `@` membuat penerapan decorator lebih ringkas dan memudahkan pembaca mengenali perilaku tambahan yang melekat pada suatu fungsi.
Meskipun demikian, decorator memiliki keterbatasan. Penggunaan beberapa lapisan decorator dapat membuat alur pelaksanaan fungsi lebih sulit ditelusuri, terutama jika setiap lapisan mengubah argumen, hasil, atau kondisi pelaksanaan. Programmer perlu memahami urutan penerapan dan pelaksanaan decorator agar tidak terjadi kesalahan.
Decorator juga perlu dirancang dengan memperhatikan parameter dan nilai balik. Jika pembungkus tidak meneruskan argumen secara benar atau tidak mengembalikan hasil fungsi asli, perilaku fungsi dapat berubah secara tidak sengaja. Penggunaan `functools.wraps` juga penting untuk membantu mempertahankan metadata fungsi.
Oleh karena itu, decorator sebaiknya digunakan ketika perilaku tambahan memang perlu dipisahkan dan diterapkan secara konsisten. Untuk tugas yang sangat sederhana, penambahan decorator mungkin tidak diperlukan. Pemilihan teknik harus mempertimbangkan keterbacaan, kemudahan pengujian, serta kebutuhan pemeliharaan program.
Cara kerja decorator didukung oleh kemampuan Python memperlakukan fungsi sebagai objek. Fungsi dapat diteruskan sebagai argumen, disimpan dalam variabel, dan dikembalikan oleh fungsi lain. Fungsi bertingkat serta closure juga berperan penting karena memungkinkan pembungkus mempertahankan akses terhadap fungsi asli dan variabel yang diperlukan.
Simbol `@` menyediakan sintaks ringkas untuk menerapkan decorator. Decorator dapat digunakan pada fungsi tanpa parameter maupun fungsi yang memiliki parameter, asalkan pembungkus dirancang sesuai kebutuhan. Penggunaan `*args` dan `**kwargs` membuat pembungkus lebih fleksibel, sedangkan `functools.wraps` membantu mempertahankan informasi fungsi asli.
Selain itu, Python memungkinkan beberapa decorator diterapkan pada satu fungsi dan menyediakan pola decorator berargumen untuk mengatur perilaku tambahan. Penerapannya mencakup pencatatan aktivitas, pengukuran waktu eksekusi, validasi masukan, dan berbagai kebutuhan lain dalam pengembangan program.
Dengan memahami pengertian, prinsip kerja, dan contoh penerapannya, programmer dapat menggunakan decorator secara tepat untuk menghasilkan kode yang lebih ringkas, terstruktur, dan mudah dipelihara. Penguasaan konsep ini juga menjadi bekal penting untuk mempelajari berbagai pustaka dan kerangka kerja Python yang memanfaatkan decorator dalam pengelolaan fungsi, metode, serta perilaku program.
from functools import wraps
def validasi_positif(fungsi):
@wraps(fungsi)
def pembungkus(panjang, lebar):
if panjang <= 0 or lebar <= 0:
raise ValueError(
"Panjang dan lebar harus lebih besar dari nol"
)
return fungsi(panjang, lebar)
return pembungkus
@validasi_positif
def luas_persegi_panjang(panjang, lebar):
return panjang * lebar
print(luas_persegi_panjang(8, 5))
Hasil program:
40
Decorator `validasi_positif()` memeriksa kedua parameter sebelum fungsi utama dijalankan. Jika salah satu nilai tidak memenuhi syarat, program menghasilkan `ValueError`. Jika seluruh nilai memenuhi persyaratan, fungsi asli dijalankan dan hasilnya dikembalikan.
Penggunaan `raise ValueError` membuat kesalahan masukan dinyatakan secara eksplisit. Pendekatan ini umumnya lebih tepat daripada hanya menampilkan pesan lalu melanjutkan program seolah-olah operasi berhasil.
Dalam aplikasi nyata, validasi dapat mencakup pemeriksaan jenis data, rentang angka, format teks, atau persyaratan lain. Namun, decorator validasi harus dirancang sesuai dengan tanda tangan fungsi dan aturan data yang diterapkan. Jika fungsi memiliki bentuk parameter berbeda-beda, pembungkus dapat dibuat lebih fleksibel dengan menggunakan `*args` dan `**kwargs`.
Kelebihan dan Keterbatasan Decorator
Decorator memiliki sejumlah kelebihan yang membuatnya bermanfaat dalam pengembangan program. Salah satu kelebihannya adalah mengurangi pengulangan kode. Perilaku tambahan dapat ditempatkan pada satu decorator dan diterapkan pada beberapa fungsi yang membutuhkan perilaku serupa.Kelebihan berikutnya adalah memisahkan tanggung jawab. Fungsi utama dapat berfokus pada tugas pokoknya, sedangkan decorator menangani proses tambahan seperti pencatatan aktivitas, validasi, pengukuran waktu, atau pemeriksaan akses. Pemisahan tersebut dapat membuat struktur program lebih teratur.
Decorator juga mendukung penggunaan kembali kode. Satu decorator dapat diterapkan pada berbagai fungsi yang sesuai, sehingga programmer tidak perlu menyalin instruksi tambahan pada setiap bagian program. Selain itu, sintaks `@` membuat penerapan decorator lebih ringkas dan memudahkan pembaca mengenali perilaku tambahan yang melekat pada suatu fungsi.
Meskipun demikian, decorator memiliki keterbatasan. Penggunaan beberapa lapisan decorator dapat membuat alur pelaksanaan fungsi lebih sulit ditelusuri, terutama jika setiap lapisan mengubah argumen, hasil, atau kondisi pelaksanaan. Programmer perlu memahami urutan penerapan dan pelaksanaan decorator agar tidak terjadi kesalahan.
Decorator juga perlu dirancang dengan memperhatikan parameter dan nilai balik. Jika pembungkus tidak meneruskan argumen secara benar atau tidak mengembalikan hasil fungsi asli, perilaku fungsi dapat berubah secara tidak sengaja. Penggunaan `functools.wraps` juga penting untuk membantu mempertahankan metadata fungsi.
Oleh karena itu, decorator sebaiknya digunakan ketika perilaku tambahan memang perlu dipisahkan dan diterapkan secara konsisten. Untuk tugas yang sangat sederhana, penambahan decorator mungkin tidak diperlukan. Pemilihan teknik harus mempertimbangkan keterbacaan, kemudahan pengujian, serta kebutuhan pemeliharaan program.
Kesimpulan
Decorator merupakan fitur Python yang memungkinkan programmer menambahkan atau mengubah perilaku suatu fungsi tanpa harus mengubah secara langsung kode utama fungsi tersebut. Dalam penerapan yang umum, decorator menerima fungsi sebagai argumen, mendefinisikan fungsi pembungkus, kemudian mengembalikan fungsi pembungkus tersebut agar dapat digunakan sebagai pengganti fungsi asli.Cara kerja decorator didukung oleh kemampuan Python memperlakukan fungsi sebagai objek. Fungsi dapat diteruskan sebagai argumen, disimpan dalam variabel, dan dikembalikan oleh fungsi lain. Fungsi bertingkat serta closure juga berperan penting karena memungkinkan pembungkus mempertahankan akses terhadap fungsi asli dan variabel yang diperlukan.
Simbol `@` menyediakan sintaks ringkas untuk menerapkan decorator. Decorator dapat digunakan pada fungsi tanpa parameter maupun fungsi yang memiliki parameter, asalkan pembungkus dirancang sesuai kebutuhan. Penggunaan `*args` dan `**kwargs` membuat pembungkus lebih fleksibel, sedangkan `functools.wraps` membantu mempertahankan informasi fungsi asli.
Selain itu, Python memungkinkan beberapa decorator diterapkan pada satu fungsi dan menyediakan pola decorator berargumen untuk mengatur perilaku tambahan. Penerapannya mencakup pencatatan aktivitas, pengukuran waktu eksekusi, validasi masukan, dan berbagai kebutuhan lain dalam pengembangan program.
Dengan memahami pengertian, prinsip kerja, dan contoh penerapannya, programmer dapat menggunakan decorator secara tepat untuk menghasilkan kode yang lebih ringkas, terstruktur, dan mudah dipelihara. Penguasaan konsep ini juga menjadi bekal penting untuk mempelajari berbagai pustaka dan kerangka kerja Python yang memanfaatkan decorator dalam pengelolaan fungsi, metode, serta perilaku program.
Artikel ini akan dibaca oleh: Firza Tegar Febrian, Gadis Lolalita, Gustaf Bintang Prayoga, Hanik Arifah, dan Haninda Cahya Ramadhani.
40 komentar untuk "Memahami Decorator dalam Python"
Hubungi admin melalui Wa : +62-896-2414-6106
Respon komentar 7 x 24 jam, mohon bersabar jika komentar tidak langsung dipublikasi atau mendapatkan balasan secara langsung.
Bantu admin meningkatkan kualitas blog dengan melaporkan berbagai permasalahan seperti typo, link bermasalah, dan lain sebagainya melalui kolom komentar.
- Ikatlah Ilmu dengan Memostingkannya -
Apa yang dimaksud dengan "green inflation"?
BalasHapusGreen inflation merujuk pada peningkatan biaya yang terkait dengan kebijakan atau tindakan yang bertujuan mendukung keberlanjutan dan perlindungan lingkungan.
HapusApa yang menjadi penyebab utama green inflation?
BalasHapusFaktor penyebab green inflation melibatkan investasi dalam teknologi hijau, perubahan kebijakan yang mendukung lingkungan, dan upaya untuk mengurangi dampak ekologis.
HapusBagaimana teknologi hijau dapat berkontribusi terhadap green inflation?
BalasHapusPenerapan teknologi hijau seringkali memerlukan investasi awal yang tinggi, sehingga dapat menaikkan biaya produksi atau layanan.
HapusApa peran kebijakan dalam green inflation?
BalasHapusKebijakan pro lingkungan, seperti pajak karbon atau insentif untuk energi terbarukan, dapat menciptakan green inflation dengan mengubah dinamika biaya dalam perekonomian.
HapusBagaimana green inflation dapat memengaruhi sektor ekonomi tertentu?
BalasHapusGreen inflation dapat memberikan tekanan tambahan pada sektor-sektor ekonomi tertentu, terutama yang sangat tergantung pada sumber daya energi atau praktik konvensional.
HapusApa dampak green inflation terhadap konsumen?
BalasHapusGreen inflation dapat menyebabkan kenaikan harga barang dan layanan, memberikan dampak langsung pada konsumen dengan meningkatkan biaya hidup.
HapusBagaimana perubahan perilaku konsumen dapat mempengaruhi green inflation?
BalasHapusJika konsumen lebih memilih produk dan layanan ramah lingkungan, ini dapat mendorong lebih banyak perusahaan untuk mengadopsi praktik hijau, tetapi juga dapat meningkatkan permintaan dan harga.
HapusApa peran pendidikan masyarakat dalam mengatasi green inflation?
BalasHapusPendidikan masyarakat dapat meningkatkan kesadaran tentang pentingnya praktik-praktik ramah lingkungan, sehingga masyarakat lebih siap mendukung perubahan tersebut.
HapusMengapa kebijakan pro-lingkungan dapat menyebabkan ketidaksetaraan sosial?
BalasHapusPeningkatan biaya untuk industri tertentu dapat menciptakan ketidaksetaraan ekonomi, terutama jika tidak ada langkah-langkah untuk melibatkan atau melindungi kelompok masyarakat dengan pendapatan rendah.
HapusBagaimana pemerintah dapat merancang kebijakan yang seimbang untuk mengatasi green inflation?
BalasHapusPemerintah perlu mempertimbangkan dampak ekonomi dan sosial dari kebijakan pro-lingkungan, mencari keseimbangan antara perlindungan lingkungan dan stabilitas ekonomi.
HapusApa dampak green inflation terhadap investasi dalam inovasi teknologi hijau?
BalasHapusGreen inflation dapat mendorong investasi dalam inovasi teknologi hijau, tetapi juga dapat menimbulkan tantangan finansial bagi perusahaan untuk mengadopsi teknologi tersebut.
HapusBagaimana perubahan iklim dapat mempengaruhi green inflation?
BalasHapusPerubahan iklim dapat meningkatkan urgensi untuk mengadopsi praktik-praktik ramah lingkungan, tetapi juga dapat menciptakan tekanan finansial tambahan untuk mengatasi dampak perubahan iklim.
HapusMengapa transparansi dalam kebijakan lingkungan penting untuk mengurangi ketidakpastian ekonomi?
BalasHapusTransparansi dapat membantu pelaku ekonomi memahami dampak kebijakan lingkungan, mengurangi ketidakpastian, dan memberikan kejelasan mengenai arah ekonomi.
HapusBagaimana pelaku bisnis dapat beradaptasi dengan green inflation?
BalasHapusBisnis dapat beradaptasi dengan green inflation dengan mengadopsi praktik-praktik ramah lingkungan, meningkatkan efisiensi operasional, dan berinovasi untuk mengurangi dampak lingkungan.
HapusApa peran lembaga keuangan dalam mendukung proyek-proyek yang berkelanjutan?
BalasHapusLembaga keuangan dapat mendukung proyek-proyek yang berkelanjutan melalui pemberian pinjaman atau investasi pada inisiatif yang mendukung tujuan keberlanjutan.
HapusMengapa perlu adanya kerja sama internasional dalam mengatasi green inflation?
BalasHapusIsu lingkungan melibatkan batas negara, dan kerja sama internasional diperlukan untuk mengatasi tantangan keberlanjutan secara global.
HapusBagaimana pendekatan sektor swasta dapat membantu mengatasi green inflation?
BalasHapusSektoral swasta dapat berperan dengan mengadopsi praktik-praktik bisnis yang berkelanjutan, berinvestasi dalam inovasi hijau, dan berpartisipasi dalam kemitraan untuk mendukung keberlanjutan.
HapusMengapa penting bagi perusahaan untuk melibatkan pemangku kepentingan dalam kebijakan hijau mereka?
BalasHapusMelibatkan pemangku kepentingan membantu menciptakan dukungan untuk kebijakan hijau, mengurangi resistensi, dan membangun reputasi positif di mata konsumen dan masyarakat.
HapusApa dampak peningkatan permintaan akan produk hijau terhadap harga?
BalasHapusPeningkatan permintaan dapat mendorong kenaikan harga produk hijau, memberikan insentif bagi lebih banyak perusahaan untuk beralih ke praktik-praktik yang ramah lingkungan.
HapusBagaimana masyarakat dapat berkontribusi dalam mengatasi green inflation?
BalasHapusMasyarakat dapat berkontribusi dengan meningkatkan kesadaran, mendukung produk dan layanan hijau, serta mengambil tindakan kecil dalam kehidupan sehari-hari untuk mengurangi dampak lingkungan.
Hapus