Lewati ke isi
Profil penulisSeri Buku GIS Kehutanan dan Pertanian/ PY

BAB 11: Cetakan dan Pembungkus: Objek, Kelas, dan Dekorator

Studi kasus: "Kok perintah QGIS ditulis dengan titik di tengah?" #

Analis Muda membaca skrip PyQGIS dan melihat baris seperti petak.featureCount() dan layer.fields(). Titik di tengah dan kurung di ujung itu apa artinya? Di sebagian skrip, ada pula kata class dan tanda @ di atas fungsi. Semua itu membuka satu gagasan yang membuat PyQGIS dan ArcPy terasa masuk akal: objek.

Konsep: kelas dan objek dalam tiga kalimat #

Sebuah kelas adalah cetakan, dan sebuah objek adalah barang yang dicetak darinya. Bayangkan cetakan kue: satu cetakan, banyak kue, dan tiap kue punya isinya sendiri. Objek membawa isi (disebut atribut) dan kemampuan (disebut metode), dan keduanya diakses dengan titik: objek.isi dan objek.kemampuan().

Ilustrasi 11.1: Kelas sebagai cetakan
Skema kelas Petak sebagai cetakan, dan dua objek p1 dan p2 yang masing-masing punya nama dan luas sendiri

Istilah baru di bagian ini:

  • Kelas (class): cetakan yang dibuat dengan kata class.
  • Objek: hasil cetakan. Bisa banyak, dan masing-masing punya isi sendiri.
  • Atribut: isi yang menempel pada objek, misalnya p1.luas_ha.
  • Metode: fungsi yang menempel pada objek, misalnya p1.kategori().

Membuat kelas sendiri #

PYTHONUnduh berkas
class Petak:
    def __init__(self, kode, luas_ha):
        self.kode = kode
        self.luas_ha = luas_ha

    def luas_m2(self):
        return self.luas_ha * 10000

    def kategori(self):
        if self.luas_ha > 5:
            return "besar"
        return "kecil"


p1 = Petak("P-01", 4.0)
p2 = Petak("P-02", 8.0)
print(p1.kode, p1.luas_m2(), p1.kategori())
print(p2.kode, p2.luas_m2(), p2.kategori())

Penjelasannya:

  • class Petak: membuat cetakan bernama Petak.
  • def __init__(self, kode, luas_ha): adalah pembuat objek. Python memanggilnya otomatis saat Anda menulis Petak(...). Dua garis bawah di kiri dan kanan namanya itu wajib.
  • self berarti "objek yang sedang dibuat atau dipakai". Ia selalu menjadi parameter pertama sebuah metode. Anda tidak mengisinya sendiri saat memanggil.
  • self.kode = kode menyimpan bahan ke dalam objek sebagai atribut.
  • luas_m2 dan kategori adalah metode: fungsi biasa yang dipasang di dalam kelas.
  • p1 = Petak("P-01", 4.0) mencetak satu objek. p2 mencetak yang lain. Isi mereka terpisah.
HASIL
P-01 40000.0 kecil
P-02 80000.0 besar

Pewarisan: kelas turunan #

Sebuah kelas boleh mewarisi kelas lain: ia mengambil semua isi dan kemampuannya, lalu mengganti sebagian. Tulis nama kelas induk di dalam kurung.

PYTHONUnduh berkas
class Petak:
    def __init__(self, kode, luas_ha):
        self.kode = kode
        self.luas_ha = luas_ha

    def luas_m2(self):
        return self.luas_ha * 10000

    def kategori(self):
        return "biasa"


class PetakLindung(Petak):
    def kategori(self):
        return "lindung"


p3 = PetakLindung("P-03", 2.0)
print(p3.kode, p3.kategori(), p3.luas_m2())

PetakLindung tidak menulis ulang __init__ dan luas_m2: ia mengambilnya dari Petak. Ia hanya mengganti kategori.

HASIL
P-03 lindung 20000.0

Pewarisan inilah yang dipakai QGIS. Skrip algoritma Processing di Buku M4 diawali class KerapatanHotspot(QgsProcessingAlgorithm):. Artinya: kelas baru Anda mewarisi seluruh kemampuan dasar algoritma QGIS, lalu Anda mengisi bagian yang khas, seperti nama dan parameternya.

Dekorator: pembungkus fungsi #

Sebuah dekorator adalah pembungkus yang dipasang di atas fungsi dengan tanda @. Bayangkan kotak kado: fungsi di dalamnya sama, tetapi ada tambahan pada pembungkusnya.

PYTHONUnduh berkas
def catat(fungsi):
    def bungkus(*args, **kwargs):
        print("memanggil", fungsi.__name__)
        return fungsi(*args, **kwargs)
    return bungkus


@catat
def ke_hektar(m2):
    return m2 / 10000


print(ke_hektar(40000))

Penjelasannya:

  • catat menerima sebuah fungsi, lalu mengembalikan fungsi pembungkus yang mencetak catatan sebelum menjalankan yang asli.
  • @catat di atas def ke_hektar sama dengan menulis ke_hektar = catat(ke_hektar).
  • *args dan **kwargs menampung semua argumen apa pun, lalu meneruskannya.
HASIL
memanggil ke_hektar
4.0

Anda hampir tidak perlu menulis dekorator sendiri. Yang penting, Anda bisa mengenalinya di skrip orang lain: tanda @ di atas def berarti "fungsi ini dibungkus dengan fasilitas tertentu".

Skrip yang dibedah: dua gaya algoritma Processing #

Di Buku M4 ada dua skrip yang membuat alat baru untuk QGIS. Skrip pertama bergaya kelas, skrip kedua bergaya dekorator. Di Bagian B (Bab 27) keduanya dibahas lengkap dan dijalankan. Di sini kita hanya mengenali bentuknya.

Tujuan. Mengenali kelas dan dekorator di skrip nyata.

Bahan. Tidak ada. Potongan ini tidak dijalankan, karena butuh Processing QGIS.

Potongan 1: gaya kelas (kerapatan_hotspot.py).

PYTHONUnduh berkas
class KerapatanHotspot(QgsProcessingAlgorithm):
    PETAK = "PETAK"
    TITIK = "TITIK"
    BATAS = "BATAS"
    HASIL = "HASIL"

    def createInstance(self):
        return KerapatanHotspot()

    def name(self):
        return "kerapatan_hotspot"

Penjelasan baris demi baris. Nomor baris dihitung dari potongan di atas.

BarisTulisanArtinya
1class KerapatanHotspot(QgsProcessingAlgorithm):Buat kelas baru yang mewarisi QgsProcessingAlgorithm, cetakan algoritma milik QGIS.
2-5PETAK = "PETAK" dan tiga baris lainEmpat atribut tetap berupa teks. Nama berhuruf besar menandakan nilainya tidak berubah. Dipakai sebagai nama parameter.
7-8def createInstance(self): return KerapatanHotspot()Metode yang wajib ada. Ia mencetak satu salinan baru kelas ini bila QGIS memintanya.
10-11def name(self): return "kerapatan_hotspot"Metode yang memberi nama alat. QGIS memanggilnya sendiri.

Potongan 2: gaya dekorator (tambah_luas_ha.py).

PYTHONUnduh berkas
@alg(name="tambah_luas_ha", label="Tambah kolom luas (hektare)", group="kehutanan", group_label="Kehutanan")
@alg.input(type=alg.SOURCE, name="INPUT", label="Layer poligon", types=[2])
@alg.input(type=alg.SINK, name="OUTPUT", label="Hasil")
def tambah_luas_ha(instance, parameters, context, feedback, inputs):
BarisTulisanArtinya
1@alg(name=..., label=..., group=...)Dekorator yang menjadikan fungsi di bawahnya sebuah alat QGIS, lengkap dengan nama, label, dan kelompoknya.
2@alg.input(type=alg.SOURCE, ...)Dekorator tambahan: menyatakan satu masukan berupa layer poligon.
3@alg.input(type=alg.SINK, ...)Dekorator tambahan: menyatakan satu keluaran berupa layer hasil.
4def tambah_luas_ha(...)Fungsi yang dibungkus. Isinya menghitung luas dan menulis hasilnya.

Hasil. Potongan ini tidak mencetak apa-apa. Di Bab 27 Anda akan melihat alat-alat ini muncul di kotak peralatan Processing QGIS.

Bagian yang boleh diubah. Nama, label, dan grup alat boleh Anda ganti dengan tulisan sendiri. Nama parameter (misalnya "INPUT") harus sama di semua tempat yang memakainya.

Jebakan.

  • Lupa self. Metode yang tidak menyebut self sebagai parameter pertama menghasilkan TypeError saat dipanggil, karena Python tetap menyodorkan objeknya.
  • Lupa mengisi atribut lewat self. Menulis kode = kode di __init__ hanya membuat kotak sementara. Isinya hilang. Tulis self.kode = kode.
  • Salah mewarisi. Kelas turunan harus menyebut induknya di dalam kurung. Tanpa itu, ia tidak mendapat kemampuan induk.

Latihan variasi. Tambahkan metode kerapatan(self, jumlah_titik) ke kelas Petak pada contoh di atas. Metode itu mengembalikan jumlah titik dibagi luas hektare. Uji dengan petak 8 hektare dan 4 titik.

Contoh jawaban:

PYTHONUnduh berkas
class Petak:
    def __init__(self, kode, luas_ha):
        self.kode = kode
        self.luas_ha = luas_ha

    def kerapatan(self, jumlah_titik):
        return jumlah_titik / self.luas_ha


p = Petak("P-02", 8.0)
print(p.kerapatan(4))
HASIL
0.5

Cek paham #

  1. Apa beda kelas dan objek?
  2. Apa arti self di dalam sebuah metode?
  3. Apa yang dilakukan tanda @ di atas sebuah fungsi?

Jawaban.

  1. Kelas adalah cetakan. Objek adalah barang yang dicetak dari cetakan itu, dan tiap objek punya isi sendiri.
  2. self berarti objek yang sedang dipakai. Lewat self, metode membaca dan mengubah isi objek itu.
  3. Menandai bahwa fungsi di bawahnya dibungkus oleh dekorator, yaitu fungsi lain yang menambahkan fasilitas.

Kesalahan umum #

  • Memanggil metode tanpa kurung. p1.kategori hanya menunjuk fungsinya. Perbaikan: tulis p1.kategori().
  • Mengira dua objek berbagi isi. Tiap objek membawa isinya sendiri. Perbaikan: ubah isi lewat objek yang dimaksud.
  • Takut pada class dan @. Perbaikan: pada tahap ini cukup kenali bentuknya. Menulisnya sendiri baru dibutuhkan di Bab 27.

Ringkasan dan latihan #

  • Kelas adalah cetakan, objek adalah barang cetakan. Objek punya atribut (isi) dan metode (kemampuan).
  • __init__ membuat objek, self menunjuk objek itu.
  • Pewarisan mengambil isi kelas induk, dekorator membungkus fungsi.

Latihan. Buat kelas Layer dengan atribut nama dan jumlah_fitur, serta metode ringkas() yang mengembalikan kalimat "<nama> memuat <jumlah> fitur". Buat dua objek, lalu cetak ringkasan keduanya.

Tabel perbandingan: tiga hal yang mirip tetapi berbeda #

HalFungsiMetode
LetakBerdiri sendiriDi dalam kelas
Cara memanggilke_hektar(40000)p1.luas_m2()
Bahan khususHanya parameterSelalu ada self