Praktik Baik dan Buruk saat Menulis kode Python

rafeqm

Muhammad Rafiqi

Posted on February 17, 2022

Praktik Baik dan Buruk saat Menulis kode Python

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Keren, kan? Tapi masih bisa lebih keren lagi:

>>> a, *y, e = x
>>> a, e, y
(1, 16, [2, 4, 8])
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Kita gunakan unpacking biar lebih elegan :

>>> x, y, z = 2, 2, 2
Enter fullscreen mode Exit fullscreen mode

Namun, menggunakan chained assignments membuat pekerjaan kita lebih mudah:

>>> x = y = z = 2
>>> x, y, z
(2, 2, 2)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Namun, ada cara yang lebih baik untuk mengiterasi sebuah sequence:

>>> for item in x:
...     print(item)
... 
1
2
4
8
16
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Eits! Kita balikkan saja urutan sequence-nya untuk meringkas kode:

>>> for item in x[::-1]:
...     print(item)
... 
16
8
4
2
1
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Begini cara kita menggunakannya untuk mendapatkan dan mengatur state suatu objek:

>>> c = C()
>>> c.set_x(2)
>>> c.get_x()
2
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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`
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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`
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
rafeqm
Muhammad Rafiqi

Posted on February 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related