Praktik Baik dan Buruk saat Menulis kode Python
Muhammad Rafiqi
Posted on February 17, 2022
Python adalah bahasa pemrograman multi-paradigma tingkat tinggi yang menekankan readability. Bahasa pemrograman ini umumnya dikembangkan, dipelihara, dan sering digunakan sesuai dengan konvensi yang disebut "The Zen of Python" atau PEP 20.
Hi, Selamat datang di siRafeq. Pada artikel ini, kita akan melihat beberapa contoh kebiasaan baik dan buruk dalam menulis kode Python yang mungkin sering Anda temui.
Gunakan Unpacking dan Chaining untuk Menulis Kode yang Ringkas
Unpacking
Packing dan unpacking adalah salah satu fitur Python yang sangat berguna. Kita dapat menggunakan unpacking untuk menetapkan nilai variabel kita:
>>> a, b = 2, 'my-string'
>>> a
2
>>> b
'my-string'
Dengan fitur ini, kita juga bisa menukar nilai variabel dengan cara yang mungkin paling ringkas dan elegan di seluruh dunia pemrograman:
>>> a, b = b, a
>>> a
'my-string'
>>> b
2
Luar biasa! Unpacking juga dapat digunakan untuk mengisi nilai ke beberapa variabel dalam kasus yang lebih kompleks. Misalnya, Anda bisa menginisialisasi seperti ini:
>>> x = (1, 2, 4, 8, 16)
>>> a = x[0]
>>> b = x[1]
>>> c = x[2]
>>> d = x[3]
>>> e = x[4]
>>> a, b, c, d, e
(1, 2, 4, 8, 16)
Oke ribet ya begitu? Kita akan menggunakan pendekatan yang lebih ringkas dan bisa dibilang lebih mudah dibaca:
>>> a, b, c, d, e = x
>>> a, b, c, d, e
(1, 2, 4, 8, 16)
Keren, kan? Tapi masih bisa lebih keren lagi:
>>> a, *y, e = x
>>> a, e, y
(1, 16, [2, 4, 8])
Intinya, variabel dengan prefix *
'mengumpulkan' nilai-nilai yang tidak diberikan kepada variabel lain.
Chaining
Dengan Python, kita bisa menghubungkan (chaining) operasi perbandingan. Jadi, kita gak perlu meriksa apakah dua atau lebih perbandingan bernilai True
:
>>> x = 4
>>> x >= 2 and x <= 8
True
Sebagai gantinya, kita tulis kode di atas dalam bentuk yang lebih ringkas, seperti yang dilakukan ahli matematika:
>>> 2 <= x <= 8
True
>>> 2 <= x <= 3
False
Python juga mendukung assignment beruntun. Jadi, jika kita ingin mengisi nilai yang sama ke beberapa variabel, daripada kita dapat melakukannya dengan cara seperti ini:
>>> x = 2
>>> y = 2
>>> z = 2
Kita gunakan unpacking biar lebih elegan :
>>> x, y, z = 2, 2, 2
Namun, menggunakan chained assignments membuat pekerjaan kita lebih mudah:
>>> x = y = z = 2
>>> x, y, z
(2, 2, 2)
Hati-hati dengan cara ini karena semua variabel merujuk ke instance yang sama! Ketika salah satu nilai variabel berubah, nilai dua variabel juga ikut berubah. Oleh karena itu, cara unpacking bisa jadi alternatif yang lebih aman.
Mengecek Nilai None
None
adalah objek khusus dan unik dalam Python yang juga memiliki fungsi yang sama seperti null
dalam bahasa yang mirip seperti C (C#, Javascript dkk.).
Kita bisa memeriksa apakah suatu variabel adalah None
dengan operator perbandingan ==
dan !=
:
>>> x, y = 2, None
>>> x == None
False
>>> y == None
True
>>> x != None
True
>>> y != None
False
Namun, cara yang lebih pythonic dan umum adalah menggunakan is
dan not
:
>>> x is None
False
>>> y is None
True
>>> x is not None
True
>>> y is not None
False
Selain itu, kita lebih baik menggunakan cara is not
(x is not None
) daripada alternatifnya, not
, yang kurang mudah dibaca (x is None
).
Iterasi pada Sequences dan Mappings
Kita dapat menerapkan iterasi atau looping dengan Python dalam beberapa cara. Python menyediakan beberapa class bawaan untuk memfasilitasinya.
Di hampir semua kasus, kita bisa menggunakan range()
untuk mendapatkan iterator yang menghasilkan integer:
>>> x = [1, 2, 4, 8, 16]
>>> for i in range(len(x)):
... print(x[i])
...
1
2
4
8
16
Namun, ada cara yang lebih baik untuk mengiterasi sebuah sequence:
>>> for item in x:
... print(item)
...
1
2
4
8
16
Bagaimana jika kita ingin mengiterasi sequence dalam urutan terbalik? Tentu saja, kita gunakan opsi range()
lagi:
>>> for i in range(len(x)-1, -1, -1):
... print(x[i])
...
16
8
4
2
1
Eits! Kita balikkan saja urutan sequence-nya untuk meringkas kode:
>>> for item in x[::-1]:
... print(item)
...
16
8
4
2
1
Biar pythonic, kita gunakan reversed()
untuk mendapatkan iterator yang menghasilkan item dari sequence dalam urutan terbalik:
>>> for item in reversed(x):
... print(item)
...
16
8
4
2
1
Acapkali, kita membutuhkan sebuah item dari sequence dan indeksnya sekaligus:
>>> for i in range(len(x)):
... print(i, x[i])
...
0 1
1 2
2 4
3 8
4 16
Kalau begini, lebih baik menggunakan enumerate()
untuk mendapatkan iterator lain yang menghasilkan _tuple _ dengan indeks dan itemnya:
>>> for i, item in enumerate(x):
... print(i, item)
...
0 1
1 2
2 4
3 8
4 16
Wah, keren ya? Tapi bagaimana jika kita ingin mengiterasi lebih dari dua atau lebih sequence? Tentu saja, kita dapat menggunakan range()
lagi:
>>> y = 'abcde'
>>> for i in range(len(x)):
... print(x[i], y[i])
...
1 a
2 b
4 c
8 d
16 e
Untuk kasus seperti ini, Python juga menawarkan solusi yang lebih baik. Kita dapat menerapkan zip()
dan mendapatkan tuple dari item yang sesuai:
>>> for item in zip(x, y):
... print(item)
...
(1, 'a')
(2, 'b')
(4, 'c')
(8, 'd')
(16, 'e')
Dan kita juga dapat mengombinasikannya dengan unpacking:
>>> for x_item, y_item in zip(x, y):
... print(x_item, y_item)
...
1 a
2 b
4 c
8 d
16 e
Harap diingat bahwa range()
bisa sangat berguna. Namun, ada kasus (seperti yang ditunjukkan di atas) di mana ada alternatif yang lebih mudah. Contohnya, mengiterasi _dictionary _ menghasilkan key-nya:
>>> z = {'a': 0, 'b': 1}
>>> for k in z:
... print(k, z[k])
...
a 0
b 1
Daripada begitu, kita pakai saja method .items()
untuk mendapatkan tuple dengan key dan value sekaligus:
>>> for k, v in z.items():
... print(k, v)
...
a 0
b 1
Kita bisa juga menggunakan method .keys()
dan .values()
untuk mengiterasi masing-masing key dan value pada sebuah dictionary.
Membandingkan dengan Nol
Saat kita memiliki data numerik dan kita harus memeriksa apakah angkanya sama dengan 0 (nol), kita bisa tapi tidak harus memakai operator pembanding ==
dan !=
seperti ini:
>>> x = (1, 2, 0, 3, 0, 4)
>>> for item in x:
... if item != 0:
... print(item)
...
1
2
3
4
Kita gunakan cara yang pythonic dengan mengingat bahwa nol ditafsirkan sebagai False
dalam konteks Boolean, sementara semua angka lainnya dianggap True
:
>>> bool(0)
False
>>> bool(-1), bool(1), bool(20), bool(28.4)
(True, True, True, True)
Dengan mengingat hal ini, kita bisa gunakan if item
alih-alih if item != 0
:
>>> for item in x:
... if item:
... print(item)
...
1
2
3
4
Kita bisa mengikuti logika yang sama dan menggunakan if not item
alih-alih if item == 0
.
Hindari Penggunaan Objek Mutable pada Argumen Opsional
Sistem Python sangatlah fleksibel dalam memberikan argumen pada fungsi dan method. Argumen opsional merupakan bagian dari kelebihan ini. Tapi hati-hati! Kita seharusnya tidak menggunakan argumen opsional yang bersifat mutable. Perhatikan contoh berikut:
>>> def f(value, seq=[]):
... seq.append(value)
... return seq
Di sini kita bisa lihat, sepertinya jika kita tidak mengisi seq
, f()
akan menambahkan value
ke list kosong dan mengembalikan sesuatu seperti [value]
:
>>> f(value=2)
[2]
Kayaknya gak ada masalah kan? Pasti ada! Coba lihat contoh berikut:
>>> f(4)
[2, 4]
>>> f(8)
[2, 4, 8]
>>> f(16)
[2, 4, 8, 16]
Terkejut? Bingung? Jika ya, Anda bukan satu-satunya. Karena _instance _ yang sama dari argumen opsional (list seq[]
dalam kasus ini) dirujuk setiap kali fungsi dipanggil. Oke, mungkin terkadang Anda maunya seperti apa yang dilakukan kode di atas. Namun, umumnya Anda harus menghindarinya. Kita dapat menghindarinya dengan beberapa logika tambahan. Salah satu caranya adalah ini:
>>> def f(value, seq=None):
... if seq is None:
... seq = []
... seq.append(value)
... return seq
Versi yang lebih pendeknya bisa dibuat dengan mengganti bagian if seq is None
dengan if not seq
. Sekarang, kita mendapatkan perilaku yang berbeda:
>>> f(value=2)
[2]
>>> f(value=4)
[4]
>>> f(value=8)
[8]
>>> f(value=16)
[16]
Dalam kebanyakan kasus, beginilah yang kita diinginkan.
Hindari Getter dan Setter Klasik
Python membolehkan kita mendefinisikan method getter dan setter sama seperti C++ dan Java:
>>> class C:
... def get_x(self):
... return self.__x
...
... def set_x(self, value):
... self.__x = value
Begini cara kita menggunakannya untuk mendapatkan dan mengatur state suatu objek:
>>> c = C()
>>> c.set_x(2)
>>> c.get_x()
2
Pada kasus tertentu, inilah cara terbaik untuk menyelesaikan masalah. Namun, agar lebih elegan kita akan mendefinisikan dan menggunakan property
, terutama dalam kasus sederhana:
... @property
... def x(self):
... return self.__x
...
... @x.setter
... def x(self, value):
... self.__x = value
Properti dianggap lebih pythonic daripada getter dan setter klasik. Kita bisa menggunakannya dengan cara yang sama seperti di C#, yaitu seperti atribut data biasa:
>>> c = C()
>>> c.x = 2
>>> c.x
2
Jadi, umumnya, menggunakan properti merupakan praktik yang baik. Gunakan getter dan setter seperti C++ saat memang diperlukan.
Hindari Mengakses Protected Class Members
Python tidak memiliki class member privat. Namun, ada aturan yang menyatakan bahwa kita tidak boleh mengakses atau memodifikasi member yang dimulai dengan garis bawah () di luar _instance mereka. Tidak ada jaminan bahwa mereka akan mempertahankan perilaku yang ada.
Misalnya, perhatikan kode berikut:
>>> class C:
... def __init__(self, *args):
... self.x, self._y, self.__z = args
...
>>> c = C(1, 2, 4)
Instance kelas C
ini memiliki tiga member data: .x
, ._y
, dan ._C__z
. Kenapa ada ._C__z
, dan ke mana perginya .__z
? Jika nama member dimulai dengan double underscore (dunder), namanya malah jadi kacau, alias termodifikasi otomatis oleh python. Itu sebabnya kita punya ._C__z
alih-alih .__z
. OK sekarang kita bisa mengakses atau merubah .x
secara langsung:
>>> c.x # OK
1
Kita juga bisa mengakses atau memodifikasi ._y
dari luar instance-nya, tapi ini dianggap sebagai praktik yang buruk:
>>> c._y # Bisa, tapi praktik buruk!
2
Kita tidak bisa lagi mengakses .__z
karena namanya sudah dirubah Python jadi ._C__z
. Jadi kita bisa mengakses atau memodifikasi ._C__z
:
>>> c.__z # Error!
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'C' object has no attribute '__z'
>>> c._C__z # Bisa, tapi begini malah lebih buruk!
4
Anda harus benar-benar menghindari ini. Penulis class ini kemungkinan besar memulai nama atribut dengan garis bawah untuk memberi tahu Anda, "jangan digunakan!".
Bebaskan Sumber Daya Memori Menggunakan Context Managers
Kadang kala kita perlu menulis kode untuk mengelola sumber daya dengan tepat. Kasus ini sering terjadi saat bekerja dengan file, koneksi database, atau entitas lain dengan sumber daya yang tak terkelola. Misalnya, kita dapat membuka file dan memprosesnya:
>>> my_file = open('filename.csv', 'w')
>>> # do something with `my_file`
Untuk mengelola memori dengan semestinya, kita harus menutup file ini terlebih dahulu setelah menyelesaikan pekerjaan sebelum melanjutkan ke proses berikutnya:
>>> my_file = open('filename.csv', 'w')
>>> # do something with `my_file and`
>>> my_file.close()
Baiklah, menggunakan cara ini lebih baik daripada tidak melakukannya sama sekali. Namun, bagaimana jika sebuah exception terjadi saat memproses file ini sehingga my_file.close()
tidak pernah dieksekusi. Kita dapat menangani ini dengan sintaks exception handling atau dengan context managers. Cara kedua berarti kita memasukkan kode ke dalam blok with
:
>>> with open('filename.csv', 'w') as my_file:
... # do something with `my_file`
Menggunakan blok with
berarti method spesial .enter()
dan .exit()
akan dipanggil, bahkan dalam jika terjadi exception. Metode ini seharusnya pasti menjaga sumber daya memori. Kita dapat mengolah desain yang sangat kuat dengan mengombinasikan context managers dan exception handling.
Saran Gaya Penulisan
Kode Python harus elegan, ringkas, dan mudah dibaca. Intinya harus indah.
Referensi utama tentang cara menulis kode Python yang indah adalah Style Guide for Python Code atau PEP 8. Anda tentu harus membacanya jika ingin membuat kode atau ngoding dengan Python.
Kesimpulan
Artikel ini memberikan beberapa saran tentang cara menulis kode yang lebih efisien, lebih mudah dibaca, dan lebih ringkas. Singkatnya, menunjukkan cara menulis kode yang Pythonic. Selain itu, PEP 8 menyediakan panduan gaya untuk kode Python, dan PEP 20 menerangkan prinsip-prinsip bahasa Python.
Nikmati menulis kode Pythonic, bermanfaat, dan indah!
Terima kasih telah membaca.
Posted on February 17, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.