9 Dekorator Built-In Python Yang Mengoptimalkan Kode Anda Secara Signifikan - CRUDPRO

9 Dekorator Built-In Python Yang Mengoptimalkan Kode Anda Secara Signifikan

9 Dekorator Built-In Python Yang Mengoptimalkan Kode Anda Secara Signifikan

"Bentuk sederhana lebih baik daripada yang kompleks."

Fitur Python terbaik yang mengaplikasikan filosofi dari "zen of Python" ini adalah dekorator.

Dekorator bisa membantu Anda menulis code yang lebih sedikit dan lebih simpel untuk menerapkan logika kompleks dan menggunakannya kembali dimanapun.

Lebih penting lagi, ada beberapa dekorator Python bawaan yang menakjubkan yang membuat hidup kita lebih mudah, karena kita hanya bisa menggunakan satu baris code untuk menambahkan fungsionalitas sulit ke fungsi atau kelas yang ada.

Berbicara itu murah. Mari kita lihat 9 dekorator pilihan saya yang akan menunjukkan betapa elegannya Python.

1. @lru_cache: Percepat Program Anda dengan Caching

Cara termudah untuk mempercepat fungsi Python Anda dengan trik caching adalah dengan menggunakan dekorator @lru_cache.

Dekorator ini dapat digunakan untuk meng-cache hasil dari suatu fungsi, sehingga panggilan selanjutnya ke fungsi dengan argumen yang sama tidak akan dijalankan lagi.

Ini sangat membantu untuk fungsi yang mahal secara komputasi atau yang sering dipanggil dengan argumen yang sama.

Mari kita lihat contoh intuitif:

import time


def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.18129450 seconds

Program di atas menghitung angka Fibonacci ke-N dengan fungsi Python. Ini memakan waktu karena ketika Anda menghitung fibonacci(30), banyak angka Fibonacci sebelumnya akan dihitung berkali-kali selama proses rekursi.

Sekarang, mari percepat dengan dekorator @lru_cache:

from functools import lru_cache
import time


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


start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.00002990 seconds

Seperti yang ditunjukkan kode di atas, setelah menggunakan dekorator @lru_cache, kita bisa mendapatkan hasil yang sama dalam 0,00002990 detik, yang lebih cepat dari 0,18129450 detik sebelumnya.

Dekorator @lru_cache memiliki parameter ukuran maksimal yang menentukan jumlah maksimum hasil yang akan disimpan dalam cache. Saat cache penuh dan hasil baru perlu disimpan, hasil yang terakhir digunakan dikeluarkan dari cache untuk memberi ruang bagi yang baru. Ini disebut strategi yang paling baru digunakan (LRU).

Secara default, ukuran maksimum disetel ke 128. Jika disetel ke None (Tidak ada), sebagai contoh kami, fitur LRU dinonaktifkan dan cache dapat bertambah tanpa terikat.

2. @total_ordering: Dekorator Kelas yang Mengisi Metode Pengurutan yang Hilang

Dekorator @total_ordering dari modul functools digunakan untuk menghasilkan metode perbandingan yang hilang untuk kelas Python berdasarkan yang ditentukan.

Berikut contohnya:

from functools import total_ordering


@total_ordering
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __eq__(self, other):
        return self.grade == other.grade

    def __lt__(self, other):
        return self.grade < other.grade


student1 = Student("Alice", 85)
student2 = Student("Bob", 75)
student3 = Student("Charlie", 85)

print(student1 < student2)  # False
print(student1 > student2)  # True
print(student1 == student3)  # True
print(student1 <= student3) # True
print(student3 >= student2) # True

Seperti yang diilustrasikan kode di atas, tidak ada definisi untuk metode __ge__, __gt__, dan __le__ di kelas Student. Namun, terima kasih kepada dekorator @total_ordering, hasil perbandingan kami antara instance yang berbeda semuanya benar.

Manfaat dekorator ini jelas:

  • Itu dapat membuat kode Anda lebih bersih dan menghemat waktu Anda. Karena Anda tidak perlu menulis semua metode perbandingan.
  • Beberapa kelas lama mungkin tidak cukup mendefinisikan metode perbandingan. Lebih aman menambahkan dekorator @total_ordering ke dalamnya untuk penggunaan lebih lanjut.
3. @contextmanager: Membuat Pengelola Konteks Khusus

Python memiliki mekanisme pengelola konteks untuk membantu Anda mengelola sumber daya dengan benar.

Sebagian besar, kita hanya perlu menggunakan pernyataan with :

with open("test.txt",'w') as f:
    f.write("Yang is writing!")

Seperti yang ditunjukkan kode di atas, kita dapat membuka file menggunakan pernyataan with sehingga akan ditutup secara otomatis setelah ditulis. Kita tidak perlu memanggil fungsi f.close() secara eksplisit untuk menutup file.

Terkadang, kita perlu menentukan pengelola konteks khusus untuk beberapa persyaratan khusus. Dalam hal ini, dekorator @contextmanager adalah teman kita.

Misalnya, kode berikut mengimplementasikan manajer konteks kustom sederhana yang dapat mencetak informasi terkait saat file dibuka atau ditutup.

from contextlib import contextmanager

@contextmanager
def file_manager(filename, mode):
    print("The file is opening...")
    file = open(filename,mode)
    yield file
    print("The file is closing...")
    file.close()

with file_manager('test.txt', 'w') as f:
    f.write('Yang is writing!')
# The file is opening...
# The file is closing...
4. @property: Menyiapkan Getter dan Setter untuk Kelas Python

Getter dan setter adalah konsep penting dalam pemrograman berorientasi objek (OOP).

Untuk setiap variabel instan kelas, metode pengambil mengembalikan nilainya sementara metode penyetel menetapkan atau memperbarui nilainya. Mengingat hal ini, getter dan setter masing-masing juga dikenal sebagai pengakses dan mutator.

Mereka digunakan untuk melindungi data Anda agar tidak diakses atau dimodifikasi secara langsung dan tidak terduga.

Bahasa OOP yang berbeda memiliki mekanisme yang berbeda untuk mendefinisikan getter dan setter. Di Python, kita cukup menggunakan dekorator @property.

class Student:
    def __init__(self):
        self._score = 0

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, s):
        if 0 <= s <= 100:
            self._score = s
        else:
            raise ValueError('The score must be between 0 ~ 100!')

Yang = Student()

Yang.score=99
print(Yang.score)
# 99

Yang.score = 999
# ValueError: The score must be between 0 ~ 100!

Seperti yang ditunjukkan contoh di atas, variabel skor tidak dapat ditetapkan sebagai 999, yang merupakan angka yang tidak berarti. Karena kami membatasi rentang yang dapat diterima di dalam fungsi setter menggunakan dekorator @property.

Tanpa diragukan lagi, menambahkan penyetel ini dapat berhasil menghindari bug atau hasil yang tidak terduga.

5. @cached_property: Cache Hasil Metode sebagai Atribut

Python 3.8 memperkenalkan dekorator kuat baru ke modul functool@cached_property. Itu dapat mengubah metode kelas menjadi properti yang nilainya dihitung sekali dan kemudian di-cache sebagai atribut normal selama masa pakai instance.

Ini contohnya:

from functools import cached_property


class Circle:
    def __init__(self, radius):
        self.radius = radius

    @cached_property
    def area(self):
        return 3.14 * self.radius ** 2


circle = Circle(10)
print(circle.area)
# prints 314.0
print(circle.area)
# returns the cached result (314.0) directly

Pada kode di atas, kita mendekorasi metode area melalui @cached_property. Jadi tidak ada perhitungan berulang untuk circle.area dari instance yang sama dan tidak berubah.

6. @classmethod: Menentukan Metode Kelas di Kelas Python

Di dalam kelas Python, ada 3 kemungkinan jenis metode:

  • Metode Instance: metode yang terikat pada sebuah instance. Mereka dapat mengakses dan memodifikasi data instance. Metode instance dipanggil pada instance kelas, dan dapat mengakses data instance melalui parameter self.
  • Metode kelas: metode yang terikat ke kelas. Mereka tidak dapat mengubah data instance. Metode kelas dipanggil pada kelas itu sendiri, dan ia menerima kelas sebagai parameter pertama, yang biasanya bernama cls.
  • Metode statis: metode yang tidak terikat pada instance atau kelas.

Metode instance dapat didefinisikan sebagai fungsi Python normal selama parameter pertamanya adalah self. Namun, untuk mendefinisikan metode kelas, kita perlu menggunakan dekorator @classmethod.

Untuk mendemonstrasikan, contoh berikut mendefinisikan metode kelas yang dapat digunakan untuk mendapatkan instance Circle melalui diameter:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)

    @property
    def diameter(self):
        return self.radius * 2

    @diameter.setter
    def diameter(self, diameter):
        self.radius = diameter / 2


c = Circle.from_diameter(8)
print(c.radius)  # 4.0
print(c.diameter)  # 8.0
7. @staticmethod: Menentukan Metode Statis di Kelas Python

Seperti disebutkan, metode statis tidak terikat pada instance atau kelas. Mereka termasuk dalam kelas hanya karena secara logis mereka termasuk di sana.

Metode statis umumnya digunakan dalam kelas utilitas yang melakukan sekelompok tugas terkait, seperti perhitungan matematis. Dengan mengatur fungsi terkait ke dalam metode statis di dalam kelas, kode kita akan menjadi lebih teratur dan lebih mudah dipahami.

Untuk mendefinisikan metode statis, kita hanya perlu menggunakan dekorator @staticmethod. Mari kita lihat contohnya:

class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
        self.nickname = None

    def set_nickname(self, name):
        self.nickname = name

    @staticmethod
    def suitable_age(age):
        return 6 <= age <= 70


print(Student.suitable_age(99)) # False
print(Student.suitable_age(27)) # True
print(Student('yang', 'zhou').suitable_age(27)) # True
8. @dataclass: Menentukan Kelas Khusus Dengan Kode Lebih Sedikit

Dekorator @dataclass (diperkenalkan dengan Python 3.7) dapat secara otomatis menghasilkan beberapa metode khusus untuk sebuah kelas, seperti __init__, __repr__, __eq__, __lt__, dan seterusnya.

Oleh karena itu, ini dapat menghemat banyak waktu kita untuk menulis metode dasar ini. Jika kelas terutama digunakan untuk menyimpan data, dekorator @dataclass adalah teman terbaik kita.

Untuk mendemonstrasikan, contoh berikut hanya mendefinisikan dua bidang data dari kelas bernama Point. Berkat dekorator @dataclass, cukup untuk digunakan:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

point = Point(1.0, 2.0)
print(point)
# Point(x=1.0, y=2.0)
9. @atexit.register: Daftarkan Fungsi yang Akan Dijalankan Setelah Penghentian Program Normal

Dekorator @register dari modul atexit dapat memungkinkan kita menjalankan fungsi saat juru bahasa Python keluar.

Dekorator ini sangat berguna untuk melakukan tugas akhir, seperti melepaskan sumber daya atau sekadar mengucapkan selamat tinggal!

Ini contohnya:

import atexit

@atexit.register
def goodbye():
    print("Bye bye!")

print("Hello Yang!")

Outputnya adalah:

Hello Yang!
Bye bye!

Seperti yang ditunjukkan contoh, karena penggunaan dekorator @register, terminal mencetak "Bye bye!" bahkan jika kami tidak memanggil fungsi selamat tinggal secara eksplisit.

Terima kasih sudah membaca ❤