Lewati ke konten utama

Tutorial Python Descriptors

Pelajari apa itu Python Descriptors, kapan Anda harus menggunakannya, dan mengapa Anda harus menggunakannya.
Diperbarui 28 Sep 2026  · 13 mnt Baca

Jelajahi bersama AI

ChatGPTClaudePerplexity

Deskriptor Python atau secara umum, deskriptor, memberi Anda teknik yang kuat untuk menulis kode yang dapat digunakan kembali dan dibagikan antar kelas. Sekilas mirip dengan konsep pewarisan, tetapi secara teknis berbeda. Ini adalah cara serbaguna untuk mencegat akses atribut. Deskriptor adalah mekanisme di balik properti, metode statis, metode kelas, metode super, dan sebagainya.

Deskriptor ditambahkan pada Python versi 2.2, dan sejak itu dianggap sebagai hal “magis” yang memberi gaya baru pada kelas tradisional. Ini adalah kelas yang memungkinkan Anda mengelola properti di kelas lain. Secara khusus, mereka mengimplementasikan antarmuka untuk metode __get__(), __set__() dan __delete__() yang membuatnya menarik karena banyak alasan. Misalnya, dekorator kelas dan dekorator properti yang mungkin sudah Anda lihat di Python.

Sederhananya, kelas yang mengimplementasikan metode __get__(), __set()__, atau __delete()__ untuk suatu objek dikenal sebagai "Descriptor". Mengutip langsung dari dokumentasi resmi Python, deskriptor adalah atribut objek dengan perilaku binding, yaitu akses atributnya digantikan oleh metode dalam protokol deskriptor. Metode-metode tersebut adalah __get__(), __set__(), dan __delete__() (Sumber).

Perilaku binding dalam konteks deskriptor berarti pengikatan cara nilai dapat diatur (set), diambil (get), atau dihapus untuk variabel, objek, atau sekumpulan data tertentu. Seluruh interaksi ini terikat ke potongan data tersebut karena hanya berlaku pada data yang Anda tetapkan, sehingga mengikatnya pada bagian data tertentu itu saja.

Deskriptor dapat dikategorikan lebih lanjut menjadi deskriptor data dan non-data. Jika deskriptor yang Anda tulis hanya memiliki metode __get__(), maka itu adalah deskriptor non-data; sebaliknya, implementasi yang melibatkan metode __set__() dan __delete__() disebut deskriptor data. Deskriptor non-data hanya dapat dibaca, sedangkan deskriptor data dapat dibaca dan ditulis.

Penting untuk dicatat bahwa deskriptor ditetapkan pada kelas, bukan pada instans kelas. Memodifikasi kelas akan menimpa atau menghapus deskriptor itu sendiri, alih-alih memicu kodenya (IBM Developer).

Pada akhirnya, kelas deskriptor tidak terbatas hanya memiliki tiga metode ini, artinya ia juga dapat memuat atribut lain selain metode get, set, dan delete.

Mari pahami metode get, set, dan delete lebih rinci yang terinspirasi dari halaman IBM Developer ini:

  • self adalah instans deskriptor yang Anda buat (Real Python).
  • object adalah instans objek tempat deskriptor Anda terpasang (Real Python).
  • type adalah tipe objek tempat deskriptor terpasang (Real Python).
  • value adalah nilai yang ditetapkan ke atribut deskriptor. get(self, object, type) set(self, object, value) delete(self, object)
  • __get__() mengakses atribut atau saat Anda ingin mengekstrak informasi. Mengembalikan nilai atribut atau memunculkan pengecualian AttributeError jika atribut yang diminta tidak ada.

  • __set__() dipanggil pada operasi penetapan atribut yang menetapkan nilai suatu atribut. Tidak mengembalikan apa pun, tetapi dapat memunculkan pengecualian AttributeError.

  • __delete__() mengontrol operasi penghapusan, yaitu saat Anda ingin menghapus atribut dari suatu objek. Tidak mengembalikan apa pun.

Sekarang mari pahami tujuan Deskriptor dengan bantuan beberapa contoh!

Tujuan Deskriptor

Misalkan kita mendefinisikan kelas car yang memiliki tiga atribut, yaitu make, model, dan fuel_cap. Anda akan menggunakan metode __init__() untuk menginisialisasi atribut kelas. Lalu, Anda akan menggunakan fungsi magis __str__(), yang hanya akan mengembalikan keluaran dari tiga atribut yang Anda berikan ke kelas saat membuat objek.

Perhatikan bahwa metode __str__() mengembalikan representasi string dari objek. Ini dipanggil ketika fungsi print() atau str() dipanggil pada objek kelas.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car1 = Car("BMW","X7",40)
print(car1)
BMW model X7 with a fuel capacity of 40 ltr.

Seperti yang terlihat dari keluaran di atas, semuanya terlihat baik!

Sekarang mari ubah kapasitas bahan bakar mobil menjadi negatif 40.

car2 = Car("BMW","X7",-40)
print(car2)
BMW model X7 with a fuel capacity of -40 ltr.

Tunggu dulu, ada yang tidak beres, bukan? Kapasitas bahan bakar mobil tidak mungkin negatif. Namun, Python menerimanya sebagai masukan tanpa error. Ini karena Python adalah bahasa pemrograman dinamis yang tidak mendukung pemeriksaan tipe secara eksplisit.

Untuk menghindari masalah ini, mari tambahkan kondisi if di metode __init__() dan periksa apakah kapasitas bahan bakar yang dimasukkan valid atau tidak. Jika tidak valid, naikkan pengecualian ValueError.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car1 = Car("BMW","X7",40)
print(car1)
BMW model X7 with a fuel capacity of 40 ltr.
car2 = Car("BMW","X7",-40)
print(car2)
----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-22-1c3d23be72f7> in <module>
----> 1 car2 = Car("BMW","X7",-40)
      2 print(car2)


<ipython-input-20-1e154783588d> in __init__(self, make, model, fuel_cap)
      5         self.fuel_cap = fuel_cap
      6         if self.fuel_cap < 0:
----> 7             raise ValueError("Fuel Capacity can never be less than zero")
      8
      9     def __str__(self):


ValueError: Fuel Capacity can never be less than zero

Dari keluaran di atas, Anda dapat melihat bahwa untuk saat ini semuanya berjalan baik karena program menaikkan ValueError jika kapasitas bahan bakar di bawah nol.

Namun, bisa ada masalah lain, yaitu bagaimana jika fuel capacity yang dimasukkan berupa nilai float atau string. Bukan hanya kapasitas bahan bakar, tetapi juga make dan model mobil bisa berupa nilai integer. Dalam semua kasus ini, program tidak akan menaikkan pengecualian.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car(-40,"X7",40)
print(car2)
-40 model X7 with a fuel capacity of 40 ltr.

Untuk menangani kasus di atas, Anda mungkin terpikir menambahkan kondisi if lain atau menggunakan metode isinstance untuk pemeriksaan tipe.

Kali ini mari gunakan metode bawaan isinstance untuk menangani error.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if isinstance(self.make, str):
            print(self.make)
        else:
            raise ValueError("Make of the car can never be an integer")

        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
BMW
BMW model X7 with a fuel capacity of 40 ltr.
car2 = Car(-40,"X7",40)
print(car2)
----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-34-75b08cba454f> in <module>
----> 1 car2 = Car(-40,"X7",40)
      2 print(car2)


<ipython-input-31-175690bf3b98> in __init__(self, make, model, fuel_cap)
      7             print(self.make)
      8         else:
----> 9             raise ValueError("Make of the car can never be an integer")
     10
     11         if self.fuel_cap < 0:


ValueError: Make of the car can never be an integer

Bagus! Jadi Anda juga berhasil menangani error ini.

Namun, bagaimana jika Anda ingin mengubah atribut kapasitas bahan bakar menjadi negatif 40 secara eksplisit di kemudian hari. Dalam kasus ini, tidak akan berhasil, karena pemeriksaan tipe hanya dilakukan sekali di metode __init__(). Seperti Anda tahu, metode __init__() adalah konstruktor dan dipanggil hanya sekali saat Anda membuat objek dari kelas. Maka, pemeriksaan tipe kustom akan gagal di kemudian hari.

Mari pahami dengan contoh.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if isinstance(self.make, str):
            print(self.make)
        else:
            raise ValueError("Make of the car can never be an integer")

        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
BMW
BMW model X7 with a fuel capacity of 40 ltr.
car2.make = -40
print(car2)
-40 model X7 with a fuel capacity of 40 ltr.

Nah, itu dia! Anda berhasil keluar dari pemeriksaan tipe.

Sekarang pikirkan begini, bagaimana jika Anda memiliki banyak atribut lain dari mobil seperti jarak tempuh, harga, aksesori, dan sebagainya yang juga membutuhkan pemeriksaan tipe, dan Anda juga menginginkan fungsionalitas di mana beberapa atribut hanya memiliki akses baca. Bukankah itu akan sangat merepotkan?

Untuk mengatasi semua masalah di atas, Python memiliki Deskriptor yang menjadi penyelamat!

Seperti yang Anda pelajari di atas, kelas apa pun yang mengimplementasikan metode magis __get__(), __set()__, atau __delete()__ untuk objek protokol deskriptor disebut Deskriptor. Mereka juga memberi Anda kontrol tambahan atas cara sebuah atribut bekerja, misalnya apakah atribut tersebut seharusnya bisa dibaca atau ditulis.

Sekarang mari perluas contoh di atas dengan menambahkan metode Python Descriptor.

class Descriptor:
    def __init__(self):
        self.__fuel_cap = 0
    def __get__(self, instance, owner):    
        return self.__fuel_cap
    def __set__(self, instance, value):
        if isinstance(value, int):
            print(value)
        else:
            raise TypeError("Fuel Capacity can only be an integer")

        if value < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

        self.__fuel_cap = value

    def __delete__(self, instance):
        del self.__fuel_cap
class Car:
    fuel_cap = Descriptor()
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
40
BMW model X7 with a fuel capacity of 40 ltr.

Jangan khawatir jika kelas Descriptor terlihat membingungkan. Mari kita uraikan menjadi bagian-bagian kecil dan pahami apa yang dilakukan masing-masing metode.

  • Metode __init__() dari kelas Descriptor memiliki variabel lokal __fuel_cap bernilai nol. dunder atau dua garis bawah di awal artinya variabel tersebut privat. Dunder di awal hanya untuk membedakan atribut kapasitas bahan bakar milik kelas Descriptor dengan kelas Car.
  • Seperti yang kini Anda tahu, metode __get__() digunakan untuk mengambil atribut, dan ia mengembalikan variabel kapasitas bahan bakar. Ia menerima tiga argumen: objek deskriptor, instans kelas yang memuat instans objek deskriptor, yaitu car2, dan terakhir pemilik (owner), yaitu kelas tempat instans tersebut berada, yaitu kelas Car. Dalam metode ini, Anda cukup mengembalikan atribut nilai, yaitu fuel_cap yang nilainya diatur dalam metode __set__().
  • Metode __set__() dipanggil ketika nilai diatur pada atribut, dan tidak seperti metode __get__(), ia tidak mengembalikan apa pun. Ia memiliki dua argumen selain objek deskriptor itu sendiri, yaitu instans yang sama seperti pada metode __get__() dan argumen value, yaitu nilai yang Anda tetapkan pada atribut. Dalam metode ini, Anda memeriksa apakah nilai yang ingin ditetapkan ke atribut fuel_cap adalah integer atau bukan. Jika bukan, Anda menaikkan pengecualian TypeError. Lalu, dalam metode yang sama, Anda juga memeriksa apakah nilainya kurang dari nol; jika ya, Anda menaikkan pengecualian lain, kali ini ValueError. Setelah memeriksa error, Anda memperbarui atribut fuel_cap sama dengan nilai tersebut.
  • Terakhir, metode __delete__(), yang dipanggil saat atribut dihapus dari suatu objek, dan seperti metode __set__(), ia tidak mengembalikan apa pun.

Kelas Car tetap sama seperti sebelumnya. Namun, satu-satunya perubahan yang Anda lakukan adalah menambahkan instans fuel_cap dari kelas Descriptor(). Perhatikan bahwa seperti disebutkan sebelumnya, instans kelas deskriptor harus ditambahkan ke kelas sebagai atribut kelas, bukan sebagai atribut instans.

Begitu Anda menetapkan variabel lokal fuel_cap di metode __init__() ke instans fuel_cap, itu akan memanggil metode __set__() dari kelas Descriptor.

Sekarang mari ubah kapasitas bahan bakar menjadi nilai negatif dan lihat apakah program menaikkan pengecualian ValueError atau tidak.

car2 = Car("BMW","X7",-40)
print(car2)
-40



----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-115-1c3d23be72f7> in <module>
----> 1 car2 = Car("BMW","X7",-40)
      2 print(car2)


<ipython-input-107-3e1f3e97d3c7> in __init__(self, make, model, fuel_cap)
      4         self.make = make
      5         self.model = model
----> 6         self.fuel_cap = fuel_cap
      7
      8     def __str__(self):


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
     11
     12         if value < 0:
---> 13             raise ValueError("Fuel Capacity can never be less than zero")
     14
     15         self.__fuel_cap = value


ValueError: Fuel Capacity can never be less than zero

Jika Anda ingat di sini, pemeriksaan tipe gagal ketika Anda kemudian mengubah nilai atribut menjadi angka negatif karena pemeriksaan tipe hanya berlaku sekali di metode __init__(). Mari perbarui nilai fuel_cap menjadi nilai string dan lihat apakah ini menghasilkan error.

car2.fuel_cap = -1
-1



----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-120-dea9dbe96ebe> in <module>
----> 1 car2.fuel_cap = -1


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
     11
     12         if value < 0:
---> 13             raise ValueError("Fuel Capacity can never be less than zero")
     14
     15         self.__fuel_cap = value


ValueError: Fuel Capacity can never be less than zero
car2.fuel_cap = "BMW"
----------------------------------------

TypeErrorTraceback (most recent call last)

<ipython-input-121-0b316a9872c6> in <module>
----> 1 car2.fuel_cap = "BMW"


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
      8             print(value)
      9         else:
---> 10             raise TypeError("Fuel Capacity can only be an integer")
     11
     12         if value < 0:


TypeError: Fuel Capacity can only be an integer

Sempurna! Seperti yang Anda lihat, ini berfungsi ketika Anda memperbarui atribut kapasitas bahan bakar di kemudian hari.

Ada sedikit masalah pada Deskriptor, yaitu saat Anda membuat instans baru atau instans kedua dari kelas, nilai instans sebelumnya tertimpa. Alasannya karena Deskriptor ditautkan ke kelas, bukan ke instans.

Mari pahami dengan contoh di bawah ini.

car3 = Car("BMW","X7",48) #created a new instance 'car3' with different values
48

Ketika Anda mencetak instans car2, Anda akan melihat bahwa nilainya telah ditimpa oleh car3.

print(car2)
BMW model X7 with a fuel capacity of 48 ltr.

Kesimpulan

Selamat telah menyelesaikan tutorial ini.

Tutorial ini ditujukan bagi mereka yang sudah nyaman dengan Python dan bercita-cita menguasai tingkat lanjut. Sebagai latihan yang baik, Anda mungkin ingin mencari cara untuk mengatasi masalah penimpaan instans yang dibahas dalam tutorial hari ini.

Jangan ragu untuk mengajukan pertanyaan terkait tutorial ini di kolom komentar di bawah.

Jika Anda ingin mempelajari lebih lanjut tentang Python, ikuti jalur keterampilan lengkap Python Programming dari DataCamp.

Lihat juga Tutorial Fungsi Python kami.

Topik
Python

Kursus Python

{count, plural, one {Kursus} other {Kursus}}

Pengantar Python

{hours} jam
7.1M
Kuasai dasar-dasar analisis data dengan Python dalam 4 jam. Kursus online ini memperkenalkan antarmuka Python dan pustaka populer.
Lihat DetailRight Arrow
Mulai Kursus
Lihat SelengkapnyaRight Arrow
Terkait

{count, plural, one {blog} other {blogs}}

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

{count, plural, one {blog} other {blogs}}

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

{count, plural, one {blog} other {blogs}}

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

{count, plural, one {blog} other {blogs}}

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

Lihat SelengkapnyaLihat Selengkapnya