Pustaka standar Python mengikuti semboyan batteries included, yaitu sebagian besar kebutuhan umum sudah disediakan tanpa pemasangan tambahan. Dari ratusan modulnya, tiga nama muncul hampir di setiap proyek, yaitu collections untuk struktur data tambahan, itertools untuk alat bantu iterasi, dan functools untuk fungsi tingkat tinggi. Topik ini memperkenalkan alat yang paling sering dipakai dari ketiganya beserta situasi yang paling tepat menggunakannya, sebagai bekal sebelum alat bantu pihak ketiga dipasang.
Modul collections: Counter dan defaultdict#
Modul collections menyediakan tipe data container, yaitu struktur penampung lebih dari satu nilai, yang melengkapi list, tuple, dan dictionary bawaan. Dua yang paling sering dipakai adalah Counter dan defaultdict. Counter adalah subclass dictionary yang menghitung kemunculan nilai, sedangkan defaultdict adalah dictionary yang menyiapkan nilai default secara otomatis untuk kunci yang belum ada. Keduanya menyelesaikan pola pemrograman yang pada dictionary biasa membutuhkan pemeriksaan kunci yang panjang dan berulang.
from collections import Counter
kata = "satu dua tiga dua dua satu".split()
hitungan = Counter(kata)
print(hitungan)
print(hitungan.most_common(2))
print(hitungan["dua"])
print(hitungan["empat"])Counter({'dua': 3, 'satu': 2, 'tiga': 1})
[('dua', 3), ('satu', 2)]
3
0Metode most_common(2) mengembalikan dua pasangan terbanyak, diurutkan dari jumlah terbesar. Perhatikan baris terakhir, yaitu akses pada kunci empat yang tidak ada: Counter mengembalikan 0 tanpa menimbulkan KeyError, sehingga pembacaan hitungan aman dilakukan tanpa pemeriksaan keberadaan kunci. Perilaku ini berbeda dari dictionary biasa, dan perbedaannya harus diingat karena salah ketik nama kunci tidak pernah memunculkan pesan error.
from collections import defaultdict
kelompok = defaultdict(list)
jadwal = [("senin", "Ali"), ("selasa", "Budi"), ("senin", "Citra")]
for hari, nama in jadwal:
kelompok[hari].append(nama)
print(dict(kelompok))
print(kelompok["rabu"]){'senin': ['Ali', 'Citra'], 'selasa': ['Budi']}
[]Pada defaultdict(list), setiap kunci baru otomatis diisi list kosong sebelum elemen ditambahkan, sehingga perulangan cukup berisi append tanpa pemeriksaan kunci terlebih dahulu. Baris terakhir menunjukkan sisi lain perilaku itu, yaitu pembacaan kunci rabu yang belum ada justru membuat entri baru berisi list kosong. Perilaku itu diinginkan pada pola pengelompokan, sedangkan untuk pembacaan semata metode get() lebih tepat karena tidak membuat entri baru.
Modul itertools: alur data tanpa list perantara#
Modul itertools menghimpun alat bantu yang membangun iterator, yaitu objek yang menghasilkan satu nilai pada satu waktu sesuai permintaan. Karena tidak menyimpan seluruh hasil di memori, alat-alat ini hemat memori pada data yang besar, dan list() dipakai ketika hasilnya memang ingin ditampung penuh. Dua alat yang paling sering terpakai adalah accumulate dan product.
from itertools import accumulate, product
print(list(accumulate([10, 20, 30, 40])))
print(list(product("AB", repeat=2)))[10, 30, 60, 100]
[('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')]Fungsi accumulate menghasilkan total berjalan, yaitu penjumlahan yang menumpuk dari kiri ke kanan, dan pola ini sering muncul pada rekap saldo atau penghitungan posisi. Fungsi product menghasilkan kombinasi kartesian, yaitu semua pasangan yang mungkin dari kedua masukannya, sehingga perulangan bersarang dua tingkat dapat digantikan satu baris. Modul ini masih menyimpan banyak alat lain seperti chain, islice, dan groupby, yang seluruhnya mengikuti pola iterator yang sama.
Modul functools: menyimpan hasil dengan lru_cache#
Modul functools berisi alat bantu untuk fungsi, dan yang paling terkenal adalah decorator @lru_cache, yaitu cache hasil pemanggilan yang menyimpan nilai kembalian berdasarkan argumennya. Nama LRU adalah singkatan dari least recently used, yaitu kebijakan pembuangan entri yang paling lama tidak dipakai. Manfaatnya paling terlihat pada fungsi rekursif yang menghitung ulang hal yang sama berkali-kali.
pemanggilan = 0
def fibonacci(n):
global pemanggilan
pemanggilan += 1
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30))
print(pemanggilan)832040
2692537Menghitung fibonacci(30) tanpa cache membutuhkan 2692537 pemanggilan fungsi, karena cabang kiri dan kanan rekursi terus menghitung ulang nilai yang sama. Versi berikut menambahkan satu baris decorator dan menghilangkan hampir seluruh pekerjaan itu.
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30))
print(fibonacci.cache_info())832040
CacheInfo(hits=28, misses=31, maxsize=None, currsize=31)Nilai misses=31 berarti hanya ada 31 perhitungan nyata, yaitu satu untuk setiap nilai dari fibonacci(0) sampai fibonacci(30), sedangkan hits=28 adalah pemanggilan yang langsung terjawab oleh cache. Metode cache_info() melaporkan keadaan cache tersebut dan berguna untuk menilai apakah cache benar-benar dipakai. Perubahan satu baris ini memangkas jutaan pemanggilan menjadi puluhan, dan pola yang sama berlaku untuk fungsi murni, yaitu fungsi yang hasilnya hanya ditentukan oleh masukan, dengan argumen yang dapat dihitung nilai hash-nya.
Memilih alat yang tepat#
Ketiga modul tersebut menyelesaikan kebutuhan yang berbeda, sehingga pemilihannya mengikuti bentuk masalahnya. Tabel berikut merangkum padanannya.
| Kebutuhan | Alat | Modul |
|---|---|---|
| Menghitung kemunculan nilai | Counter | collections |
| Mengelompokkan data per kunci | defaultdict | collections |
| Menggabungkan dan memotong alur data | chain, islice | itertools |
| Menghasilkan kombinasi dan permutasi | product, combinations | itertools |
| Menyimpan hasil pemanggilan fungsi | lru_cache | functools |
Ketiganya bagian dari pustaka standar, sehingga seluruhnya dipakai hanya dengan satu baris import tanpa pemasangan apa pun. Kebiasaan memeriksa pustaka standar sebelum memasang package pihak ketiga menghemat ketergantungan proyek, dan pemeriksaan itu cepat karena dokumentasinya tersedia lengkap pada situs resmi Python. Contoh di atas juga menunjukkan bahwa alat sederhana dari ketiga modul ini sering menggantikan perulangan manual yang panjang dengan satu baris yang jelas maksudnya.
Kesalahan umum#
Kesalahan pertama adalah menganggap iterator itertools dapat diulang berkali-kali. Iterator mengalirkan nilai sekali saja, sehingga pemakaian kedua menghasilkan list kosong tanpa pesan error apa pun.
from itertools import chain
aliran = chain([1, 2], [3])
print(list(aliran))
print(list(aliran))[1, 2, 3]
[]Baris kedua kosong karena iterator sudah terhabiskan pada pemanggilan list() pertama. Perbaikannya adalah menyimpan hasil ke list biasa bila nilai perlu dipakai beberapa kali, atau membangun ulang iterator dari sumbernya setiap kali aliran baru dibutuhkan.
Kesalahan kedua adalah memberi argumen yang tidak dapat dihitung hash-nya pada fungsi ber-cache. lru_cache menyimpan hasil dalam dictionary yang kuncinya berupa argumen, sehingga pemanggilan dengan argumen berupa list gagal sebelum badan fungsi dijalankan.
from functools import lru_cache
@lru_cache(maxsize=None)
def jumlah_semua(nilai):
return sum(nilai)
try:
jumlah_semua([1, 2, 3])
except TypeError as e:
print(type(e).__name__, str(e))TypeError unhashable type: 'list'Perbaikannya adalah mengubah masukan menjadi tuple, yaitu tipe yang dapat dihitung hash-nya, misalnya jumlah_semua((1, 2, 3)). Aturan yang sama berlaku untuk dictionary sebagai argumen, yang sebaiknya dikirim dalam bentuk pasangan tuple yang terurut.
Kesalahan ketiga adalah mengandalkan Counter untuk memvalidasi kunci. Karena akses kunci yang tidak ada mengembalikan 0 tanpa error, salah ketik nama kunci tidak pernah terdeteksi dan hitungan nol diterima sebagai kenyataan. Pemeriksaan keberadaan kunci dengan operator in tetap diperlukan apabila keberadaan kunci itu sendiri merupakan informasi yang penting.
Ringkasan#
Countermenghitung kemunculan nilai dan mengembalikan 0 untuk kunci yang tidak ada, sedangkandefaultdictmembuat nilai default dari fungsi pabrik yang ditetapkan untuk setiap kunci baru.- Alat
itertoolsmenghasilkan iterator yang hemat memori dan hanya dapat dihabiskan satu kali, denganlist()sebagai penampung bila hasilnya perlu dipakai ulang. @lru_cachemenyimpan hasil pemanggilan berdasarkan argumennya, dancache_info()melaporkan jumlah hit serta misses-nya.- Pemeriksaan pustaka standar sebelum memasang package pihak ketiga mengurangi ketergantungan proyek secara nyata.
Referensi#
- collections — Container datatypes — Python Software Foundation
— Dokumentasi
Counter,defaultdict, dan tipe container lainnya. - itertools — Functions creating iterators for efficient looping — Python Software Foundation — Daftar lengkap alat bantu iterasi beserta resep pemakaiannya.
- functools — Higher-order functions and operations on callable objects — Python Software Foundation
— Dokumentasi
lru_cachedan alat bantu fungsi lainnya.