Kuasai metode dictionary Python dari pembuatan dan modifikasi hingga optimisasi lanjutan. Pelajari penanganan error dan skalakan kinerja untuk alur kerja produksi.
Bagi data scientist atau software engineer mana pun, kemampuan untuk memetakan hubungan antar titik data secara efisien adalah keterampilan yang tidak bisa ditawar. Jika Anda mem-parsing respons JSON kompleks dari sebuah API, mengagregasi statistik dari dataset masif, atau sekadar mengonfigurasi pengaturan aplikasi, maka dictionary bisa dibilang adalah alat terkuat Python. Dictionary mendorong manipulasi data yang bersih, mudah dibaca, dan sangat teroptimasi.
Siapa pun bisa mencari nilai di dictionary, tetapi keahlian sejati terlihat ketika Anda tahu cara menerapkan metodenya pada alur kerja data dan membuka pola-pola tingkat lanjut.
Dalam artikel ini, kita akan membahas hash table yang membuat dictionary begitu cepat, metode dictionary yang esensial, strategi penanganan error, dan teknik optimisasi kinerja.
Jika Anda baru mengenal dictionary, saya sarankan membaca tutorial dasar kami tentang Python Dictionary sebagai titik awal yang baik.
Apa Itu Dictionary Python?
Dictionary Python adalah struktur data bawaan yang dirancang untuk pencarian yang cepat dan fleksibel. Struktur ini memungkinkan Anda menyimpan dan mengambil nilai menggunakan key yang bermakna alih-alih posisi numerik, menjadikannya ideal untuk merepresentasikan data terstruktur di dunia nyata. Mari kita lihat struktur dan propertinya yang inti.
Arsitektur inti dictionary dan hashing
Sebelum masuk ke detail metode dictionary Python, ada baiknya memahami bagaimana dictionary dibangun di atas hash table. Banyak error yang akan Anda temui, seperti TypeError: unhashable type, berasal langsung dari cara kerja struktur ini.
Secara struktural, dictionary Python mengimplementasikan hash table. Pilihan arsitektur ini yang memberi kecepatan dan fleksibilitas pada dictionary. Saat Anda mendefinisikan dictionary, Anda pada dasarnya membuat array jarang, yang sering disebut bucket array.
Saat Anda menyisipkan pasangan key-value, Python meneruskan key melalui fungsi hash. Fungsi ini menghitung bilangan bulat unik (hash) yang menentukan indeks spesifik di bucket array tempat nilai akan disimpan.
Karena desain ini:
Key harus hashable, yang biasanya berarti harus bertipe immutable (mis., str, int, tuple)
Nilai boleh mutable, termasuk list, dictionary lain, atau objek kustom
Pencarian, penyisipan, dan penghapusan berjalan dalam waktu O(1) amortisasi rata-rata
Contoh berikut menunjukkan beberapa key yang valid, tetapi juga menunjukkan bahwa list tidak diterima sebagai key dictionary:
# Valid dictionary - immutable keys, any values
user_data ={"name":"Alice",# string key, string value42:[1,2,3],# integer key, list value(10,20):{"nested":True}# tuple key, dict value}print(type(user_data),"valid dict")# Invalid - will raise TypeErrortry:
invalid_dict ={[1,2]:"value"}# lists are not hashableexcept TypeError as e:print(f"Error: {e}")Didukung oleh
<class 'dict'> valid dict
Error: unhashable type: 'list'Didukung oleh
Struktur ini penting untuk kinerja. Sementara pencarian item di list mengharuskan iterasi elemen satu per satu, operasi O(n), pengambilan nilai dari dictionary rata-rata adalah operasi O(1).
Ini berarti mencari user ID dalam dataset berisi satu juta pengguna memakan waktu kira-kira sama dengan dalam dataset berisi sepuluh pengguna. Memahami perbedaan tipe data di Python adalah kunci untuk memilih struktur yang tepat untuk kasus penggunaan Anda.
Evolusi urutan dan properti
Salah satu perubahan paling signifikan dalam sejarah Python terjadi pada versi 3.7. Sebelumnya, dictionary dianggap sebagai koleksi tanpa urutan, dan iterasi atasnya bisa menghasilkan key dalam urutan yang tampak acak. Jika Anda mencetak dictionary, item mungkin muncul dalam urutan berbeda dari saat Anda menyisipkannya, bergantung pada nilai hash dan riwayat array internal.
Namun, mulai Python 3.6, dictionary mulai mempertahankan urutan penyisipan sebagai detail implementasi di CPython. Lalu, mulai Python 3.7 (dan dijamin secara resmi dalam spesifikasi bahasa), dictionary mempertahankan urutan penyisipan.
Peralihan dari mapping tanpa urutan ke berurutan ini memiliki beberapa implikasi penting untuk pengembangan Python modern. Misalnya, serialisasi JSON kini menghasilkan output yang dapat diprediksi, sehingga mempermudah debug dan memastikan reprodusibilitas data antar-run.
Jika Anda bekerja dengan pipeline data di mana urutan penting, seperti memproses event deret waktu atau mempertahankan hierarki konfigurasi, jaminan ini menghilangkan satu kelas bug halus. Selanjutnya, mari lihat cara membuat dictionary.
Membuat Dictionary Python
Membuat dictionary tampak sederhana, tetapi metode yang Anda pilih dapat memengaruhi keterbacaan dan kinerja kode. Python menyediakan berbagai cara inisialisasi, mulai dari literal sederhana hingga teknik pembangkitan terprogram tingkat lanjut untuk alur kerja data science. Mari lihat cara-cara terpentingnya.
Literal kurung kurawal
Cara paling umum dan disukai untuk membuat dictionary adalah menggunakan sintaks kurung kurawal {}. Notasi literal ini tidak hanya lebih mudah dibaca tetapi juga lebih cepat daripada metode alternatif. Python dapat mengoptimalkan konstruksi bytecode secara langsung tanpa overhead pemanggilan fungsi. Di bawah ini adalah kode yang menunjukkan dictionary:
# Preferred: Literal syntax
user_profile ={"name":"Alice","role":"Data Scientist","active":True}
user_profileDidukung oleh
{'name': 'Alice', 'role': 'Data Scientist', 'active': True}Didukung oleh
Konstruktor dict()
Namun, konstruktor dict() tak tergantikan dalam beberapa skenario. Ia bertindak sebagai pengonversi tipe, memungkinkan Anda membangun dictionary dari deret tuple atau argumen keyword. Ini sangat berguna dalam kasus-kasus berikut:
Key adalah identifier Python yang valid, tetapi Anda ingin menghindari penulisan string dengan tanda kutip
Anda perlu mentransformasi struktur data seperti list berpasangan nilai
# Using keyword arguments (cleaner for string keys)
config =dict(host="localhost", port=8080, debug=True)print(config)# Converting a list of tuples (common in data processing)
pairs =[("a",1),("b",2),("c",3)]
lookup_table =dict(pairs)print(lookup_table)Didukung oleh
Untuk skenario pembuatan dictionary yang lebih kompleks, dictionary comprehension menawarkan cara ringkas dan efisien untuk memfilter, mentransformasi, atau menghasilkan pasangan key-value secara terprogram. Ini adalah teknik esensial bagi praktisi data yang perlu memproses dan membentuk ulang data secara dinamis.
Comprehension penting untuk tugas seperti:
Membalik dictionary
Menyaring nilai null dari dataset
Mari kita lihat cara membuat dictionary comprehension di bawah ini:
# Classic use case: Creating a squares map
squares ={x: x**2for x inrange(5)}print(squares)# Filtering data during creation
raw_data ={"a":10,"b":None,"c":5}
clean_data ={k: v for k, v in raw_data.items()if v isnotNone}print(clean_data)Didukung oleh
Metode dictionary Python lain yang berguna untuk inisialisasi adalah dict.fromkeys(). Metode ini membuat dictionary baru dengan key yang ditentukan dan satu nilai yang sama. Ini sering digunakan untuk menginisialisasi pencacah atau flag status.
# Initialize multiple keys with the same default value
categories =["electronics","clothing","food","books"]
inventory =dict.fromkeys(categories,0)print(inventory)# Initialize with None for optional fields
user_fields =["email","phone","address","company"]
user_profile =dict.fromkeys(user_fields)print(user_profile)Didukung oleh
Saat menggunakan .fromkeys() dengan objek mutable seperti list atau dictionary, semua key akan mereferensikan objek yang sama di memori. Ini menciptakan jebakan “shared reference” yang dapat menimbulkan perilaku tak terduga. Mari lihat contohnya:
# DANGEROUS - all keys share the same list!
categories =["A","B","C"]
wrong_way =dict.fromkeys(categories,[])
wrong_way["A"].append(1)print(wrong_way)# CORRECT - use dictionary comprehension for independent lists
right_way ={cat:[]for cat in categories}
right_way["A"].append(1)print(right_way)Didukung oleh
Kita bisa melihat nilai yang sama dibagikan oleh semua key pada kasus pertama. Untuk menghindarinya, kita perlu menggunakan dictionary comprehension untuk list yang independen.
Metode Dictionary Python untuk Akses dan Modifikasi
Setelah dictionary dibuat, berinteraksi dengan data di dalamnya adalah salah satu tugas pemrograman harian yang paling umum. Mari lihat beberapa caranya.
Mengakses dan mengambil nilai
Ada beberapa cara berbeda untuk mengakses nilai.
Notasi bracket
Cara paling langsung untuk mengambil nilai dari dictionary adalah melalui notasi bracket d[key], yang mengembalikan nilai terkait jika key ada. Pendekatan ini ideal saat Anda yakin key ada di dictionary. Berikut kodenya:
product ={"name":"Laptop","price":1299.99,"stock":45,"category":"Electronics"}# Direct access with bracketsprint(product["name"])print(product["price"])# Attempting to access a non-existent key raises KeyErrortry:print(product["manufacturer"])except KeyError as e:print(f"Key not found: {e}")Didukung oleh
Laptop
1299.99
Key not found: 'manufacturer'Didukung oleh
Metode .get()
Untuk pengambilan yang lebih aman ketika keberadaan key tidak pasti, metode .get() memberikan solusi elegan. Metode ini mengembalikan None (atau nilai default yang ditentukan) alih-alih melempar pengecualian jika key tidak ada.
# Safe retrieval with .get()
manufacturer = product.get("manufacturer")print(manufacturer)# None# Provide a custom default value
warranty = product.get("warranty","No warranty information")print(warranty)# .get() is especially useful in data pipelines
customer_data ={"name":"John Doe","email":"[email protected]"}
phone = customer_data.get("phone","Not provided")print(phone)
address = customer_data.get("address","Not provided")print(address)Didukung oleh
None
No warranty information
Not provided
Not providedDidukung oleh
Metode .setdefault()
Metode .setdefault() menggabungkan pengambilan dan penyisipan dalam satu operasi. Ia mengambil nilai jika key ada, atau menyisipkan nilai default dan mengembalikannya jika key tidak ada—sempurna untuk pola akumulasi.
# Using .setdefault() for initialization and retrieval
page_visits ={}# First visit to 'home' - inserts 0 and returns it
count = page_visits.setdefault("home",0)print(count)
page_visits["home"]+=1# Subsequent call returns existing value
count = page_visits.setdefault("home",0)print(count)# Practical example: grouping items
inventory =[("apple","fruit"),("carrot","vegetable"),("banana","fruit"),("broccoli","vegetable")]
grouped ={}for item, category in inventory:
grouped.setdefault(category,[]).append(item)print(grouped)Didukung oleh
0
1
{'fruit': ['apple', 'banana'], 'vegetable': ['carrot', 'broccoli']}Didukung oleh
Memodifikasi dictionary
Dictionary bersifat dinamis; Anda sering perlu menambah atau menghapus data seiring program berjalan.
Menambahkan pasangan key-value dengan metode .update()
Menambahkan satu pasangan semudah assignment (d['new'] = 1), tetapi untuk operasi massal, metode .update() lebih baik. Metode ini menerima dictionary lain atau iterable pasangan key-value, dan menggabungkannya ke objek yang ada.
Mari lihat cara menggunakan metode .update():
# Simple assignment for single key-value pairs
user ={"username":"alice_2024","role":"analyst"}
user["email"]="[email protected]"# Add new key
user["role"]="senior_analyst"# Update existing key# Bulk update with .update()
user.update({"department":"Analytics","level":3})print(user)# Update from sequence of tuples
additional_info =[("projects",12),("rating",4.8)]
user.update(additional_info)print(user)# Update with keyword arguments
user.update(active=True, certified=True)print(user)Didukung oleh
Untuk panduan terperinci tentang menambahkan item, saya sarankan membaca panduan Python Dictionary Append ini.
Menghapus item dari dictionary
Python menawarkan tiga metode berbeda untuk menghapus entri dictionary, masing-masing dengan perilaku dan kasus penggunaan berbeda:
.pop(key): Menghapus key dan mengembalikan nilainya. Berguna saat Anda perlu menggunakan data tersebut untuk terakhir kalinya sebelum menghapusnya.
.popitem(): Menghapus dan mengembalikan pasangan key-value terakhir yang disisipkan (LIFO). Ini adalah manfaat langsung dari sifat terurut dictionary modern.
del d[key]: murni menghapus key. Tidak mengembalikan nilai dan sedikit lebih cepat jika nilai kembalian tidak diperlukan.
Mari lihat contoh metode-metode ini:
Metode .pop():
scores ={"Alice":95,"Bob":87,"Carol":92,"David":78}# .pop() - removes key and returns its value
alice_score = scores.pop("Alice")print(alice_score)print(scores)Didukung oleh
95
{'Bob': 87, 'Carol': 92, 'David': 78}Didukung oleh
Metode .popitem():
# .popitem() - removes and returns last inserted pair (LIFO in Python 3.7+)
scores ={"Alice":95,"Bob":87,"Carol":92,"David":78}
last_item = scores.popitem()print(last_item)print(scores)Didukung oleh
('David', 78)
{'Alice': 95, 'Bob': 87, 'Carol': 92}Didukung oleh
del:
# del statement - removes key without returning value
scores ={"Alice":95,"Bob":87,"Carol":92,"David":78}del scores["Bob"]print(scores)Didukung oleh
{'Alice': 95, 'Carol': 92, 'David': 78}Didukung oleh
Mengosongkan dictionary dengan metode .clear()
Metode .clear() mengosongkan seluruh dictionary, menyisakan objek {} kosong. Ini berbeda dari menghapus variabel itu sendiri. Objek tetap ada di memori, hanya saja kosong. Mari lihat cara kerjanya:
# .clear() - removes all items but keeps the dictionary object
scores ={"Alice":95,"Bob":87,"Carol":92,"David":78}
scores.clear()print(scores)print(type(scores))Didukung oleh
Perbedaan di antara metode-metode ini penting. Gunakan .pop() saat Anda memerlukan nilai yang dihapus, .popitem() untuk perilaku seperti stack, del untuk penghapusan sederhana, dan .clear() untuk mereset dictionary sambil mempertahankan identitasnya.
View dan iterasi
Pada versi lama Python 2, metode seperti .keys() mengembalikan list statis. Di Python versi kini (3.x), metode ini mengembalikan objek view. View adalah jendela dinamis ke dictionary. Jika dictionary berubah, view mencerminkan perubahan itu seketika tanpa perlu dipanggil ulang.
Iterasi melalui .keys(), .values(), dan .items()
Anda dapat beriterasi melalui key (.keys()), nilai (.values()), atau keduanya sekaligus menggunakan .items(). Mari lihat metode-metode ini dengan contoh:
experiment ={"model":"RandomForest","accuracy":0.94,"precision":0.91,"recall":0.89}# .keys() returns a view of all keysprint(experiment.keys())# .values() returns a view of all valuesprint(experiment.values())# .items() returns (key, value) tuples - most commonly usedfor metric, value in experiment.items():ifisinstance(value,float):print(f"{metric}: {value:.2%}")Didukung oleh
Sifat dinamis objek view berarti ia secara otomatis mencerminkan perubahan yang dibuat pada dictionary setelah view dibuat. Mari lihat contohnya:
metrics ={"MAE":0.23,"RMSE":0.45}
keys_view = metrics.keys()print(keys_view)# Add new metric
metrics["R2"]=0.87print(keys_view)Didukung oleh
dict_keys(['MAE', 'RMSE'])
dict_keys(['MAE', 'RMSE', 'R2'])Didukung oleh
Irisan dan gabungan
Fitur kuat dari objek view adalah mendukung operasi himpunan. Anda dapat melakukannya langsung pada key view untuk membandingkan dua dictionary secara efisien menggunakan operator berikut:
Intersection: &
Union: |
Difference: -
Symmetric difference/XOR: ^
Mari lihat contohnya:
dict1 ={"a":1,"b":2,"c":3}
dict2 ={"b":20,"c":30,"d":4}# Find common keys (intersection)
common_keys = dict1.keys()& dict2.keys()print(common_keys)# Find all unique keys (union)
all_keys = dict1.keys()| dict2.keys()print(all_keys)# Find keys in dict1 but not in dict2 (difference)
unique_to_dict1 = dict1.keys()- dict2.keys()print(unique_to_dict1)# Symmetric difference - keys in either but not both
exclusive_keys = dict1.keys()^ dict2.keys()print(exclusive_keys)Didukung oleh
{'b', 'c'}
{'b', 'c', 'd', 'a'}
{'a'}
{'d', 'a'}Didukung oleh
Operasi himpunan berbasis view ini jauh lebih hemat memori daripada mengonversi dictionary ke set secara eksplisit, terutama saat bekerja dengan dataset besar.
Menghindari Error dalam Metode Dictionary Python
Karena dictionary sering digunakan sebagai antarmuka utama untuk data eksternal, seperti payload JSON dari API atau file konfigurasi, dictionary merupakan sumber umum error runtime. Menulis kode yang tangguh membutuhkan lebih dari sekadar mengetahui cara mengakses data. Ini juga butuh pengetahuan cara menangani ketiadaannya.
Error dictionary paling umum adalah KeyError, yang terjadi saat mencoba mengakses key yang tidak ada. Python menawarkan dua pendekatan filosofis untuk menanganinya:
EAFP (Easier to Ask Forgiveness than Permission)
LBYL (Look Before You Leap)
EAFP: Mengelola KeyError dan pengecualian
Pola EAFP dianggap lebih Pythonic dan sering berkinerja lebih baik saat key biasanya ada, karena menghindari pengecekan berulang. Pola ini menggunakan blok try-except untuk menangani error setelah terjadi, dengan asumsi operasi biasanya akan berhasil. Mari lihat cara kerjanya:
# EAFP approach - try first, handle exceptions
user_data ={"username":"data_analyst","email":"[email protected]"}try:
phone = user_data["phone"]print(f"Phone: {phone}")except KeyError:print("Phone number not available")
phone =None# More sophisticated error handling with specific actions
config ={"host":"localhost","port":5432}try:
database = config["database"]except KeyError:print("Warning: Database not specified, using default")
database ="default_db"
config["database"]= database # Add missing configurationDidukung oleh
Phone number not available
Warning: Database not specified, using defaultDidukung oleh
Namun, ada skenario di mana gagal secara jelas, membiarkan KeyError menyebar, justru lebih disukai daripada kegagalan senyap. Dalam pendekatan LBYL, Anda secara eksplisit memeriksa keberadaan key sebelum mengaksesnya. Mari lihat contohnya:
# Critical configuration - fail loudly if missing
required_config ={"api_key":"secret123","endpoint":"api.example.com"}definitialize_api(config):# Don't catch KeyError - we WANT the program to crash if required keys are missing
api_key = config["api_key"]
endpoint = config["endpoint"]
timeout = config.get("timeout",30)# Optional with defaultreturn{"key": api_key,"endpoint": endpoint,"timeout": timeout}Didukung oleh
Memanggil fungsi ini akan memunculkan KeyError jika ada key yang hilang di dictionary, dan itu adalah perilaku yang benar karena lebih baik gagal saat inisialisasi daripada diam-diam gagal saat produksi.
Penanganan error eksplisit
Saat memproses data dari sumber eksternal seperti API atau input pengguna, penanganan error eksplisit menjadi penting. Mari lihat contohnya:
# Processing API response with defensive error handlingdefextract_user_info(api_response):"""Extract user information with comprehensive error handling."""
user_info ={}try:
user_info["id"]= api_response["user"]["id"]
user_info["name"]= api_response["user"]["profile"]["name"]except KeyError as e:print(f"Missing required field in API response: {e}")returnNone# Optional fields - use .get() with defaults
user_info["email"]= api_response.get("user",{}).get("contact",{}).get("email","N/A")
user_info["verified"]= api_response.get("user",{}).get("verified",False)return user_info
# Example usage
response ={"user":{"id":12345,"profile":{"name":"Jane Smith"},"verified":True}}
user = extract_user_info(response)print(user)Didukung oleh
{'id': 12345, 'name': 'Jane Smith', 'email': 'N/A', 'verified': True}Didukung oleh
Memahami kapan menggunakan .get() versus notasi bracket versus try-except itu penting.
LBYL: Pengujian keanggotaan secara proaktif
Pendekatan LBYL menggunakan pernyataan kondisional dengan operator in dan not in untuk memeriksa keberadaan key sebelum mencoba mengaksesnya. Pola ini lebih jelas saat berurusan dengan logika kondisional atau ketika Anda perlu mengambil tindakan berbeda berdasarkan keberadaan key. Mari lihat contohnya:
# Proactive checking with 'in' operator
student_grades ={"Alice":95,"Bob":87,"Carol":92}# Check before access
student_name ="David"if student_name in student_grades:print(f"{student_name}'s grade: {student_grades[student_name]}")else:print(f"No grade recorded for {student_name}")# Conditional update based on existenceif"David"notin student_grades:
student_grades["David"]=0# Initialize new studentprint("New student added to grading system")# Multiple key checks for validation
required_fields =["name","email","department"]
employee_record ={"name":"John Doe","email":"[email protected]"}
missing_fields =[field for field in required_fields if field notin employee_record]if missing_fields:print(f"Error: Missing required fields: {missing_fields}")else:print("All required fields present")Didukung oleh
No grade recorded for David
New student added to grading system
Error: Missing required fields: ['department']Didukung oleh
Saat memvalidasi dictionary yang berasal dari sumber eksternal seperti JSON dari API, file CSV, atau input pengguna, pengujian keanggotaan secara proaktif memberikan logika validasi yang jelas dan mudah dibaca. Mari lihat contohnya:
# Validating API response structuredefvalidate_product_data(product):"""Validate product dictionary has all required fields."""
required =["id","name","price","category"]
optional =["description","stock","manufacturer"]# Check all required fields existfor field in required:if field notin product:raise ValueError(f"Missing required field: {field}")# Validate data types for existing fieldsif"price"in product andnotisinstance(product["price"],(int,float)):raise TypeError("Price must be a number")if"stock"in product and product["stock"]<0:raise ValueError("Stock cannot be negative")returnTrue# Example usage with proper error handling
product_from_api ={"id":101,"name":"Wireless Mouse","price":29.99,"category":"Electronics","stock":150}try:if validate_product_data(product_from_api):print("Product data validated successfully")# Proceed with processingexcept(ValueError, TypeError)as e:print(f"Validation failed: {e}")Didukung oleh
Pilihan antara EAFP dan LBYL sering bergantung pada kasus penggunaan Anda. Gunakan EAFP saat operasi biasanya berhasil dan pengecualian jarang terjadi. Gunakan LBYL saat Anda memerlukan logika percabangan eksplisit atau saat memvalidasi input sebelum operasi mahal.
Sebelum melakukan iterasi atas dictionary yang berasal dari sumber eksternal, praktik terbaik adalah:
Validasi lebih awal
Periksa semua key yang wajib sekaligus
Naikkan pengecualian yang spesifik dan informatif
Jelas memisahkan field wajib dan opsional
Validasi lebih dari sekadar keberadaan jika diperlukan
Lebih memilih kegagalan eksplisit daripada fallback senyap
Dengan mengikuti praktik ini, Anda dapat membuat kode jauh lebih tangguh saat menangani data yang tidak terduga atau tidak lengkap dari API, input pengguna, atau file konfigurasi.
Untuk tinjauan lebih mendalam tentang penulisan kode Python yang tangguh, saya sarankan mengikuti kursus Writing Efficient Python Code.
Metode Dictionary Python Lanjutan
Seiring proyek data science Anda bertambah kompleks, Anda sering perlu menggabungkan, menyalin, dan mentransformasi dictionary. Mempelajari operasi lanjutan ini memungkinkan Anda memanipulasi struktur data secara efisien sambil menghindari bug halus yang dapat menggagalkan pipeline. Mari lihat beberapa metodenya.
Operator penggabungan
Python 3.9 memperkenalkan operator union yang elegan untuk menggabungkan dictionary:
| (merge operator): Membuat dictionary gabungan baru.
|= (update operator): Memperbarui dictionary yang ada secara in-place.
Operator ini menyediakan sintaks yang bersih dan mudah dibaca untuk menggabungkan data dari banyak sumber. Mari lihat contohnya:
# Union operator | creates a new merged dictionary
defaults ={"theme":"light","language":"en","notifications":True}
user_prefs ={"theme":"dark","font_size":14}
final_config = defaults | user_prefs
print(final_config)# Update operator |= modifies in place
settings ={"auto_save":True,"theme":"light"}
settings |={"theme":"dark","font_size":12}print(settings)Didukung oleh
Perilaku ini konsisten apakah Anda menggunakan |, |=, unpacking, atau metode .update(). Urutan itu penting, menjadikannya sangat berguna untuk manajemen konfigurasi berlapis (mis., default sistem < konfigurasi lingkungan < override pengguna).
Mekanisme penyalinan
Salah satu jebakan paling berbahaya adalah kesalahpahaman tentang cara Python menangani assignment variabel.
Assignment referensi membuat alias, bukan salinan
dict_a = dict_b tidak membuat salinan. Itu membuat referensi (alias). Memodifikasi yang satu akan memodifikasi yang lain. Contoh berikut mengilustrasikan konsepnya:
# Reference assignment - creates an alias, not a copy
original ={"name":"Dataset_v1","records":1000}
alias = original
# Modifying through the alias changes the original
alias["records"]=2000print(original)print(alias is original)Didukung oleh
{'name': 'Dataset_v1', 'records': 2000}
TrueDidukung oleh
Seperti terlihat, hanya ada satu dictionary dan baik original maupun alias mereferensikannya. Karenanya, saat nilai untuk key records disetel menjadi 2000, perubahan hanya berlaku pada satu dictionary ini.
Shallow copy dengan .copy() atau .dict()
Shallow copy (.copy() atau dict()) membuat objek dictionary baru, tetapi objek mutable bertingkat tetap berbagi referensi.
# Shallow copy - creates a new dict but shares nested objects
original ={"name":"Experiment_A","parameters":{"learning_rate":0.01,"epochs":100},"results":[0.85,0.89,0.92]}
shallow = original.copy()# Modifying top-level keys works as expected
shallow["name"]="Experiment_B"print(original["name"])print(shallow["name"])# But modifying nested objects affects both
shallow["parameters"]["learning_rate"]=0.001print(original["parameters"]["learning_rate"])
shallow["results"].append(0.94)print(original["results"])Didukung oleh
Experiment_A
Experiment_B
0.001
[0.85, 0.89, 0.92, 0.94]Didukung oleh
Seperti terlihat, key level-atas dimodifikasi sebagaimana mestinya, namun jika Anda memodifikasi objek mutable bertingkat, itu memengaruhi baik original maupun shallow copy.
Deep copy menggunakan copy.deepcopy()
Karena itu, shallow copy sering menyebabkan bug halus pada pipeline machine learning dan manajemen konfigurasi.
Oleh karena itu, Anda memerlukan deep copy menggunakan copy.deepcopy() dari pustaka standar untuk dictionary yang berisi objek mutable bertingkat. Ini menyalin semua objek bertingkat secara rekursif, menciptakan struktur yang sepenuhnya independen. Contoh berikut menunjukkan hal ini:
import copy
# Deep copy - creates completely independent nested structures
original ={"model":"RandomForest","hyperparameters":{"n_estimators":100,"max_depth":10,"min_samples_split":2},"feature_importance":[0.3,0.25,0.2,0.15,0.1]}
deep = copy.deepcopy(original)# Modify nested structures
deep["hyperparameters"]["n_estimators"]=200
deep["feature_importance"].append(0.05)# Original remains completely unchangedprint(original["hyperparameters"]["n_estimators"])print(len(original["feature_importance"]))print(len(deep["feature_importance"]))Didukung oleh
Dengan menerapkan teknik-teknik berikut, Anda akan memastikan integritas data dan keterpeliharaan di pipeline yang kompleks.
Gunakan | dan |= untuk penggabungan dictionary yang bersih.
Ingat bahwa yang terakhir dilihat akan menang saat terjadi benturan.
Bedakan antara assignment referensi, shallow copy, dan deep copy untuk menghindari bug halus.
Selalu utamakan copy.deepcopy() saat bekerja dengan struktur bertingkat yang mutable dalam kode produksi.
Tipe Dictionary Python yang Terspesialisasi
Walau dict standar serbaguna, pustaka standar Python menawarkan mapping terspesialisasi yang dioptimalkan untuk tugas tertentu seperti penghitungan, pengelompokan, atau penegakan struktur data. Memilih tipe terspesialisasi yang tepat dapat sangat menyederhanakan kode Anda dan mencegah satu kelas bug secara keseluruhan.
Ekstensi modul collections
Modul collections menawarkan beberapa tipe dictionary terspesialisasi yang berguna.
defaultdict
defaultdict dari modul collections menghilangkan pengecekan keberadaan key yang berulang dengan otomatis menginisialisasi key yang hilang dengan nilai default. Ini sangat berguna untuk tugas akumulasi seperti menghitung, mengelompokkan, atau membangun struktur bertingkat:
from collections import defaultdict
# Standard dict requires manual key checking
word_count ={}
text ="the quick brown fox jumps over the lazy dog".split()for word in text:if word notin word_count:
word_count[word]=0
word_count[word]+=1# defaultdict eliminates the check
word_count_auto = defaultdict(int)# int() returns 0for word in text:
word_count_auto[word]+=1# No checking needed!print(dict(word_count_auto))# Grouping with defaultdict(list)
transactions =[("2024-01-15","groceries",45.50),("2024-01-15","gas",60.00),("2024-01-16","groceries",32.75),("2024-01-16","entertainment",25.00),("2024-01-17","gas",55.00)]
by_date = defaultdict(list)for date, category, amount in transactions:
by_date[date].append((category, amount))for date, items in by_date.items():print(f"{date}: {items}")# Nested defaultdict for complex structures
nested = defaultdict(lambda: defaultdict(int))
events =[("2024-01","login",150),("2024-01","purchase",45),("2024-02","login",200),("2024-02","purchase",60)]for month, event_type, count in events:
nested[month][event_type]+= count
print(dict(nested))Didukung oleh
Kelas Counter adalah subclass dictionary terspesialisasi yang dirancang untuk menghitung objek hashable dan melakukan operasi multiset. Ini sangat kuat untuk analisis statistik dan distribusi frekuensi. Mari lihat contoh cara kerjanya:
from collections import Counter
# Count occurrences in a sequence
tags =["python","data","python","ml","data","python","statistics","ml"]
tag_counts = Counter(tags)print(tag_counts)# Most common elementsprint(tag_counts.most_common(2))# Counter arithmetic - multiset operations
skills_alice = Counter(["Python","SQL","Tableau","Python"])
skills_bob = Counter(["Python","R","SQL"])# Union (maximum of counts)
combined_skills = skills_alice | skills_bob
print(combined_skills)# Intersection (minimum of counts)
shared_skills = skills_alice & skills_bob
print(shared_skills)# Addition (sum of counts)
total_mentions = skills_alice + skills_bob
print(total_mentions)# Practical example: Analyzing survey responses
responses =["satisfied","neutral","satisfied","satisfied","dissatisfied","neutral","satisfied","very_satisfied"]
sentiment_analysis = Counter(responses)# Calculate percentage distribution
total =sum(sentiment_analysis.values())for sentiment, count in sentiment_analysis.most_common():
percentage =(count / total)*100print(f"{sentiment}: {count} ({percentage:.1f}%)")Didukung oleh
Sebelum Python 3.7, OrderedDict penting untuk mempertahankan urutan penyisipan. Meski kini dictionary standar mempertahankan urutan, OrderedDict masih memiliki kasus penggunaan tertentu, khususnya untuk pemeriksaan kesetaraan yang mempertimbangkan urutan dan untuk memindahkan item ke salah satu ujung:
from collections import OrderedDict
# OrderedDict equality considers order
dict1 ={"a":1,"b":2}
dict2 ={"b":2,"a":1}print(dict1 == dict2)
ordered1 = OrderedDict([("a",1),("b",2)])
ordered2 = OrderedDict([("b",2),("a",1)])print(ordered1 == ordered2)# Move items to beginning or end
task_queue = OrderedDict([("task1","pending"),("task2","in_progress"),("task3","pending")])# Move task3 to the beginning (highest priority)
task_queue.move_to_end("task3", last=False)print(list(task_queue.keys()))# Move task2 to the end (lowest priority)
task_queue.move_to_end("task2")print(list(task_queue.keys()))Didukung oleh
True
False
['task3', 'task1', 'task2']
['task3', 'task1', 'task2']Didukung oleh
Grafik berikut mengilustrasikan kapan memilih tipe dictionary yang mana.
Keamanan tipe modern dan view hanya-baca
Seiring adopsi Python tumbuh di lingkungan produksi skala besar, keamanan tipe menjadi isu penting. Modul typing dan types memperkenalkan cara untuk menegakkan struktur pada dictionary Anda. Memahamiing Python dunder methods juga dapat membantu Anda membangun objek mirip dictionary kustom dengan perilaku khusus.
TypedDict
Modul typing Python menyediakan TypedDict untuk mendefinisikan struktur dictionary dengan key wajib tertentu dan anotasi tipe. Ini meningkatkan dokumentasi kode, mengaktifkan autocomplete di IDE, dan menangkap error tipe selama analisis statis. Mari lihat cara kerjanya:
from typing import TypedDict
# Define strict structure for user dataclassUserProfile(TypedDict):
user_id:int
username:str
email:str
is_active:bool
role:str# Create properly typed dictionary
user: UserProfile ={"user_id":12345,"username":"data_scientist","email":"[email protected]","is_active":True,"role":"analyst"}# IDE will autocomplete and type-checkdefprocess_user(user: UserProfile)->str:# IDE knows these keys exist and their typesreturnf"User {user['username']} (ID: {user['user_id']}) - {user['role']}"# Optional keys with total=FalseclassPartialConfig(TypedDict, total=False):
host:str
port:int
database:str# All keys are optional
config: PartialConfig ={"host":"localhost"}# Valid - partial configDidukung oleh
MappingProxyType
Untuk skenario ketika Anda perlu mengekspos dictionary tetapi mencegah modifikasi, MappingProxyType membuat view read-only yang immutable dari dictionary standar. Ini sangat baik untuk melindungi konstanta konfigurasi global dari perubahan yang tidak disengaja. Mari lihat praktiknya:
from types import MappingProxyType
# Create read-only view of configuration
_INTERNAL_CONFIG ={"API_VERSION":"v2","MAX_RETRIES":3,"TIMEOUT":30,"ENDPOINTS":{"users":"/api/v2/users","data":"/api/v2/data"}}# Expose as immutable proxy
CONFIG = MappingProxyType(_INTERNAL_CONFIG)# Reading works normallyprint(CONFIG["API_VERSION"])print(CONFIG["TIMEOUT"])# Modifications raise TypeErrortry:
CONFIG["TIMEOUT"]=60except TypeError as e:print(f"Cannot modify: {e}")# Caution: nested modifications succeedtry:
CONFIG["ENDPOINTS"]["users"]="/new/endpoint"except TypeError as e:print(f"Cannot modify nested: {e}")# Practical use case: Class constantsclassDataPipeline:
_default_config ={"batch_size":1000,"parallel_workers":4,"retry_failed":True}# Expose as read-only to prevent accidental changes
DEFAULT_CONFIG = MappingProxyType(_default_config)def__init__(self, custom_config=None):# Merge with custom config while keeping defaults safe
self.config ={**self.DEFAULT_CONFIG,**(custom_config or{})}Didukung oleh
v2
30
Cannot modify: 'mappingproxy' object does not support item assignmentDidukung oleh
Jadi, seperti yang terlihat, MappingProxyType membuat dictionary itu sendiri hanya-baca (tidak bisa menambah, menghapus, atau mengikat ulang key), tetapi tidak membekukan nilai mutable yang disimpan di dalamnya. Untuk mencegah perubahan pada struktur bertingkat, Anda juga harus membuat objek bertingkat tersebut immutable atau membungkusnya dalam view hanya-baca masing-masing.
Tanda tangan fungsi dan type hint
Untuk tanda tangan fungsi dan type hint, gunakan Dict dan Mapping dari modul typing untuk mendokumentasikan struktur dictionary yang diharapkan. Kita bisa melakukannya seperti di bawah ini:
from typing import Dict, List, Mapping, Any
# Dict for mutable dictionariesdefprocess_scores(scores: Dict[str,float])-> Dict[str,str]:"""Convert numeric scores to letter grades."""
grades ={}for student, score in scores.items():if score >=90:
grades[student]="A"elif score >=80:
grades[student]="B"elif score >=70:
grades[student]="C"else:
grades[student]="F"return grades
# Mapping for read-only or general mapping typesdefdisplay_config(config: Mapping[str, Any])->None:"""Display configuration - accepts any mapping type."""for key, value in config.items():print(f"{key}: {value}")# Works with dict, MappingProxyType, OrderedDict, etc.
display_config({"host":"localhost","port":5432})
display_config(CONFIG)# MappingProxyType from earlierDidukung oleh
Saat dataset Anda tumbuh dari ribuan menjadi jutaan rekaman, efisiensi kode menjadi penting. Karena itu, perlu memahami karakteristik kinerja dictionary. Jadi, mari lihat kompleksitas komputasi, implikasi memori, dan strategi optimisasi untuk operasi dictionary.
Analisis kompleksitas waktu
Operasi dictionary mencapai kecepatannya melalui implementasi hash table, memberikan kompleksitas waktu O(1) rata-rata untuk tiga operasi paling umum:
Mengambil nilai (get)
Menyisipkan atau memperbarui entri (set)
Menghapus entri (delete)
Performa waktu konstan ini berarti operasi-operasi tersebut memakan waktu kira-kira pada orde yang sama secara rata-rata, apakah dictionary Anda berisi 10 item atau 10 juta. Mari lihat dengan contoh kode:
import time
import statistics
defbenchmark_lookup(size, repeats=50_000):"""
Measure the median time for a single dictionary lookup in a dictionary of given size.
Parameters:
size (int): Number of elements in the dictionary to create
repeats (int): How many times to repeat the lookup (for more stable median)
Returns:
float: Median lookup time in microseconds (μs)
"""# Create a large dictionary with string keys and integer values
large_dict ={f"key_{i}": i for i inrange(size)}# The key we will look up repeatedly (last element)
target_key =f"key_{size -1}"# Store individual measurement times (in nanoseconds)
times =[]# Perform many lookups to reduce measurement noisefor _ inrange(repeats):# Use high-resolution timer (nanoseconds)
start = time.perf_counter_ns()
_ = large_dict[target_key]# The actual dictionary lookup
end = time.perf_counter_ns()
times.append(end - start)# Calculate median time to minimize impact of outliers
median_ns = statistics.median(times)# Convert nanoseconds to microsecondsreturn median_ns /1000# Sizes to test (from 100k to 10 million elements)
sizes =[100_000,1_000_000,10_000_000]print("Dictionary lookup benchmark (median time over many repeats)\n")print(f"{'Size':>12} | {'Median Lookup Time':>18} | Notes")print("-"*50)for size in sizes:
lookup_time_us = benchmark_lookup(size)print(f"{size:>12,} | {lookup_time_us:>15.2f} μs | "f"{'→ still ~constant'if size == sizes[-1]else''}")Didukung oleh
Dictionary lookup benchmark (median time over many repeats)
Size | Median Lookup Time | Notes
--------------------------------------------------
100,000 | 0.14 μs |
1,000,000 | 0.14 μs |
10,000,000 | 0.14 μs | → still ~constantDidukung oleh
Waktu lookup median di atas dapat berbeda bagi Anda bergantung pada sumber daya komputasi. Poin kuncinya adalah apa pun nilainya, mereka hampir tetap sama dengan sedikit variabilitas sehingga perbedaannya dapat diabaikan, membuktikan properti O(1).
Kasus rata-rata vs terburuk
Namun, dalam skenario terburuk, kompleksitas dapat menurun menjadi O(n) ketika terjadi tabrakan hash berlebih. Tabrakan hash terjadi ketika key berbeda menghasilkan nilai hash yang sama, memaksa Python mencari melalui beberapa entri yang disimpan pada bucket hash yang sama. Mari lihat contohnya:
# Pathological case: forcing hash collisionsclassBadHash:"""Object with intentionally poor hash function."""def__init__(self, value):
self.value = value
def__hash__(self):return1# All instances hash to same value - worst case!def__eq__(self, other):returnisinstance(other, BadHash)and self.value == other.value
# This will have O(n) performance due to collisions
bad_dict ={BadHash(i): i for i inrange(1000)}# Compare with well-distributed hashes
good_dict ={f"key_{i}": i for i inrange(1000)}# Benchmark the difference
start = time.perf_counter()
_ = bad_dict[BadHash(999)]
bad_time = time.perf_counter()- start
start = time.perf_counter()
_ = good_dict["key_999"]
good_time = time.perf_counter()- start
print(f"Bad hash lookup: {bad_time *1_000_000:.2f} μs")print(f"Good hash lookup: {good_time *1_000_000:.2f} μs")print(f"Performance degradation: {bad_time / good_time:.1f}x slower")Didukung oleh
Bad hash lookup: 582.48 μs
Good hash lookup: 93.99 μs
Performance degradation: 6.2x slowerDidukung oleh
Sekali lagi, hasil pasti dan degradasi kinerja akan bervariasi tergantung daya komputasi Anda. Tetapi kecenderungannya sama: lookup dengan hash buruk akan memakan waktu jauh lebih lama daripada yang mencapai kompleksitas waktu O(1).
Pengubahan ukuran dictionary
Biaya tersembunyi lain adalah pengubahan ukuran. Saat dictionary tumbuh, Python secara otomatis mengubah ukuran hash table internal untuk mempertahankan kinerja. Operasi pengubahan ukuran ini memiliki biaya komputasi, karena memerlukan rehash semua key yang ada dan mendistribusikannya kembali ke array yang lebih besar.
Python menggunakan strategi faktor pertumbuhan, biasanya setidaknya menggandakan ukuran saat ambang tertentu tercapai. Memahami pola pengubahan ukuran ini membantu saat menginisialisasi dictionary besar. Jika Anda tahu perkiraan ukuran akhir, Anda dapat melakukan pra-alokasi ruang untuk menghindari beberapa operasi pengubahan ukuran.
Manajemen memori dan pengukuran ukuran
Kecepatan sering datang dengan biaya memori. Dictionary memiliki overhead memori signifikan dibanding tuple atau list karena harus menyimpan struktur hash table (indeks, hash, key, dan nilai). Mari pahami dengan contoh kode:
import sys
# Compare memory footprint of different data structures
data_list =[("name","Alice"),("age",30),("city","NYC")]
data_tuple =(("name","Alice"),("age",30),("city","NYC"))
data_dict ={"name":"Alice","age":30,"city":"NYC"}print(f"List of tuples: {sys.getsizeof(data_list)} bytes")print(f"Tuple of tuples: {sys.getsizeof(data_tuple)} bytes")print(f"Dictionary: {sys.getsizeof(data_dict)} bytes")Didukung oleh
List of tuples: 88 bytes
Tuple of tuples: 64 bytes
Dictionary: 184 bytesDidukung oleh
Menggunakan __slots__ untuk mengurangi ukuran objek
Pada contoh di atas, Anda dapat melihat dictionary menggunakan ~2x-3x lebih banyak memori untuk data yang sama. Untuk kelas yang membuat banyak instance, menggunakan __slots__ alih-alih __dict__ instance dapat sangat mengurangi konsumsi memori.
Secara default, Python menyimpan atribut instance dalam dictionary yang dapat diakses melalui __dict__, tetapi __slots__ menggunakan struktur berbasis array yang lebih ringkas. Mari lihat contohnya:
import sys
# Regular class - uses __dict__ for attributesclassRegularUser:def__init__(self, user_id, name, email):
self.user_id = user_id
self.name = name
self.email = email
# Optimized class - uses __slots__classOptimizedUser:
__slots__ =['user_id','name','email']def__init__(self, user_id, name, email):
self.user_id = user_id
self.name = name
self.email = email
# Create instances
regular = RegularUser(12345,"Alice","[email protected]")
optimized = OptimizedUser(12345,"Alice","[email protected]")# Compare memory usageprint(f"Regular instance: {sys.getsizeof(regular.__dict__)} bytes (__dict__)")print(f"Optimized instance: {sys.getsizeof(optimized)} bytes (__slots__)")# For massive instance counts, the savings multiply
regular_users =[RegularUser(i,f"User{i}",f"user{i}@example.com")for i inrange(1000)]
optimized_users =[OptimizedUser(i,f"User{i}",f"user{i}@example.com")for i inrange(1000)]
regular_total =sum(sys.getsizeof(u.__dict__)for u in regular_users)
optimized_total =sum(sys.getsizeof(u)for u in optimized_users)print(f"\n1000 regular instances: {regular_total:,} bytes")print(f"1000 optimized instances: {optimized_total:,} bytes")print(f"Memory savings: {((regular_total - optimized_total)/ regular_total *100):.1f}%")Didukung oleh
Terakhir, meski dictionary sangat baik untuk akses acak, struktur ini tidak dioptimalkan untuk pemrosesan data kolumnar. Jika Anda menangani data tabular skala besar (mis., jutaan baris), bermigrasi ke pandas DataFrame adalah pilihan bijak, karena dioptimalkan untuk efisiensi memori dan kecepatan ter-vektorisasi.
import pandas as pd
import time
import sys
n =10_000# Dict
dict_data ={i:{"user_id": i,"score": i *1.5,"category":f"cat_{i %10}"}for i inrange(n)}# Optimized DF: use category dtype for strings, int32 for ids
df_data = pd.DataFrame({"user_id": pd.Series(range(n), dtype="int32"),"score":[i *1.5for i inrange(n)],"category": pd.Series([f"cat_{i %10}"for i inrange(n)], dtype="category")})
dict_memory = sys.getsizeof(dict_data)
df_memory = df_data.memory_usage(deep=True).sum()print(f"Dictionary: {dict_memory:,} bytes")print(f"Optimized DataFrame: {df_memory:,} bytes")# Bulk operation: mean score per category
start = time.perf_counter()
df_mean = df_data.groupby("category")["score"].mean()
df_time =(time.perf_counter()- start)*1_000_000# Equivalent in dict (manual loop)
start = time.perf_counter()from collections import defaultdict
means = defaultdict(lambda:[0,0])for row in dict_data.values():
cat = row["category"]
means[cat][0]+= row["score"]
means[cat][1]+=1
dict_mean ={k: s/c for k,(s, c)in means.items()}
dict_time =(time.perf_counter()- start)*1_000_000print(f"\nDF groupby mean: {df_time:.2f} μs")print(f"Dict manual mean: {dict_time:.2f} μs")Didukung oleh
Anda dapat dengan jelas melihat bahwa DataFrame mengungguli dictionary pada contoh kita. Untuk alur kerja data science yang membutuhkan pencarian cepat sekaligus operasi analitis, pertimbangkan pendekatan hibrida seperti menggunakan dictionary untuk pengindeksan dan akses cepat, lalu konversi ke DataFrame untuk analisis massal.
Kunci optimisasi adalah mencocokkan struktur data dengan pola akses Anda. Jika Anda sering melakukan pencarian berbasis key, dictionary adalah yang optimal. Untuk operasi kolumnar, pemfilteran, dan agregasi pada dataset besar, DataFrame memberikan kinerja yang baik.
Kesimpulan
Dictionary lebih dari sekadar wadah penyimpanan sederhana. Struktur ini adalah perekat yang menyatukan aplikasi data science yang kompleks. Jika Anda membangun tabel lookup cepat untuk skrip atau merancang pipeline data ber-throughput tinggi, maka dictionary kemungkinan merupakan alat paling berharga Anda.
Namun, bergantung pada perilaku default tanpa penanganan error yang tangguh adalah resep kegagalan runtime. Pola penanganan error yang kuat, seperti menggunakan pendekatan EAFP versus LBYL dan validasi proaktif, sangat mungkin mencegah kegagalan runtime saat memproses data eksternal.
Koleksi terspesialisasi seperti defaultdict, Counter, dan TypedDict membuat kode Anda naik kelas dari fungsional menjadi siap produksi. Selalu perhatikan kompleksitas waktu dan manajemen memori untuk memastikan kode Anda berjalan efisien. Juga, ingat bahwa optimisasi adalah proses iteratif.
Untuk terus membangun keterampilan Python Anda, saya sarankan mengikuti Intermediate Python untuk materi lebih lanjut tentang struktur data, atau jalur karier Python Developer untuk lintasan belajar yang komprehensif.
Python Dictionary Methods FAQs
Apa keunggulan menggunakan metode .get() dibandingkan notasi bracket?⌄
Metode .get() dengan aman mengembalikan None (atau nilai default kustom) jika key tidak ada, sedangkan notasi bracket d[key] akan membuat program crash dengan melempar KeyError.
Bagaimana cara kerja metode .setdefault()?⌄
Metode ini menggabungkan pengambilan dan penyisipan dalam satu langkah. Jika key ada, ia mengembalikan nilai saat ini. Jika key hilang, ia menyisipkannya dengan nilai default yang Anda tentukan dan mengembalikan nilai baru tersebut.
Apa perbedaan antara .pop() dan .popitem()?⌄
Metode .pop(key) menghapus key tertentu dan mengembalikan nilainya. Metode .popitem() menghapus dan mengembalikan pasangan key-value terakhir yang disisipkan, mengikuti pola LIFO (Last-In, First-Out).
Apa cara paling efisien untuk menggabungkan banyak item baru ke dalam dictionary?
⌄
Gunakan metode .update(). Metode ini memungkinkan Anda menambahkan atau menimpa banyak pasangan key-value sekaligus dengan meneruskan dictionary lain, iterable tuple, atau argumen keyword.
Apakah hasil yang dikembalikan oleh .keys(), .values(), dan .items() berupa list statis?⌄
Tidak, pada Python modern (3.x), metode-metode ini mengembalikan objek view dinamis. Jika Anda menambah atau menghapus item dari dictionary, view tersebut langsung mencerminkan perubahan tanpa perlu dipanggil lagi.
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.
Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Dari pengujian kode dan penerapan kontrol versi hingga pengambilan data web dan pengembangan paket, ambil langkah selanjutnya dalam perjalanan Anda sebagai pengembang Python!
Bagi data scientist atau software engineer mana pun, kemampuan untuk memetakan hubungan antar titik data secara efisien adalah keterampilan yang tidak bisa ditawar. Jika Anda mem-parsing respons JSON kompleks dari sebuah API, mengagregasi statistik dari dataset masif, atau sekadar mengonfigurasi pengaturan aplikasi, maka dictionary bisa dibilang adalah alat terkuat Python. Dictionary mendorong manipulasi data yang bersih, mudah dibaca, dan sangat teroptimasi.
Siapa pun bisa mencari nilai di dictionary, tetapi keahlian sejati terlihat ketika Anda tahu cara menerapkan metodenya pada alur kerja data dan membuka pola-pola tingkat lanjut.
Dalam artikel ini, kita akan membahas hash table yang membuat dictionary begitu cepat, metode dictionary yang esensial, strategi penanganan error, dan teknik optimisasi kinerja.
Jika Anda baru mengenal dictionary, saya sarankan membaca tutorial dasar kami tentang Python Dictionary sebagai titik awal yang baik.
Apa Itu Dictionary Python?
Dictionary Python adalah struktur data bawaan yang dirancang untuk pencarian yang cepat dan fleksibel. Struktur ini memungkinkan Anda menyimpan dan mengambil nilai menggunakan key yang bermakna alih-alih posisi numerik, menjadikannya ideal untuk merepresentasikan data terstruktur di dunia nyata. Mari kita lihat struktur dan propertinya yang inti.
Arsitektur inti dictionary dan hashing
Sebelum masuk ke detail metode dictionary Python, ada baiknya memahami bagaimana dictionary dibangun di atas hash table. Banyak error yang akan Anda temui, seperti
TypeError: unhashable type, berasal langsung dari cara kerja struktur ini.Secara struktural, dictionary Python mengimplementasikan hash table. Pilihan arsitektur ini yang memberi kecepatan dan fleksibilitas pada dictionary. Saat Anda mendefinisikan dictionary, Anda pada dasarnya membuat array jarang, yang sering disebut bucket array.
Saat Anda menyisipkan pasangan key-value, Python meneruskan key melalui fungsi hash. Fungsi ini menghitung bilangan bulat unik (hash) yang menentukan indeks spesifik di bucket array tempat nilai akan disimpan.
Karena desain ini:
Key harus hashable, yang biasanya berarti harus bertipe immutable (mis.,
str,int,tuple)Nilai boleh mutable, termasuk list, dictionary lain, atau objek kustom
Pencarian, penyisipan, dan penghapusan berjalan dalam waktu O(1) amortisasi rata-rata
Contoh berikut menunjukkan beberapa key yang valid, tetapi juga menunjukkan bahwa list tidak diterima sebagai key dictionary:
Struktur ini penting untuk kinerja. Sementara pencarian item di list mengharuskan iterasi elemen satu per satu, operasi O(n), pengambilan nilai dari dictionary rata-rata adalah operasi O(1).
Ini berarti mencari user ID dalam dataset berisi satu juta pengguna memakan waktu kira-kira sama dengan dalam dataset berisi sepuluh pengguna. Memahami perbedaan tipe data di Python adalah kunci untuk memilih struktur yang tepat untuk kasus penggunaan Anda.
Evolusi urutan dan properti
Salah satu perubahan paling signifikan dalam sejarah Python terjadi pada versi 3.7. Sebelumnya, dictionary dianggap sebagai koleksi tanpa urutan, dan iterasi atasnya bisa menghasilkan key dalam urutan yang tampak acak. Jika Anda mencetak dictionary, item mungkin muncul dalam urutan berbeda dari saat Anda menyisipkannya, bergantung pada nilai hash dan riwayat array internal.
Namun, mulai Python 3.6, dictionary mulai mempertahankan urutan penyisipan sebagai detail implementasi di CPython. Lalu, mulai Python 3.7 (dan dijamin secara resmi dalam spesifikasi bahasa), dictionary mempertahankan urutan penyisipan.
Peralihan dari mapping tanpa urutan ke berurutan ini memiliki beberapa implikasi penting untuk pengembangan Python modern. Misalnya, serialisasi JSON kini menghasilkan output yang dapat diprediksi, sehingga mempermudah debug dan memastikan reprodusibilitas data antar-run.
Jika Anda bekerja dengan pipeline data di mana urutan penting, seperti memproses event deret waktu atau mempertahankan hierarki konfigurasi, jaminan ini menghilangkan satu kelas bug halus. Selanjutnya, mari lihat cara membuat dictionary.
Membuat Dictionary Python
Membuat dictionary tampak sederhana, tetapi metode yang Anda pilih dapat memengaruhi keterbacaan dan kinerja kode. Python menyediakan berbagai cara inisialisasi, mulai dari literal sederhana hingga teknik pembangkitan terprogram tingkat lanjut untuk alur kerja data science. Mari lihat cara-cara terpentingnya.
Literal kurung kurawal
Cara paling umum dan disukai untuk membuat dictionary adalah menggunakan sintaks kurung kurawal
{}. Notasi literal ini tidak hanya lebih mudah dibaca tetapi juga lebih cepat daripada metode alternatif. Python dapat mengoptimalkan konstruksi bytecode secara langsung tanpa overhead pemanggilan fungsi. Di bawah ini adalah kode yang menunjukkan dictionary:Konstruktor dict()
Namun, konstruktor
dict()tak tergantikan dalam beberapa skenario. Ia bertindak sebagai pengonversi tipe, memungkinkan Anda membangun dictionary dari deret tuple atau argumen keyword. Ini sangat berguna dalam kasus-kasus berikut:Dictionary comprehension
Untuk skenario pembuatan dictionary yang lebih kompleks, dictionary comprehension menawarkan cara ringkas dan efisien untuk memfilter, mentransformasi, atau menghasilkan pasangan key-value secara terprogram. Ini adalah teknik esensial bagi praktisi data yang perlu memproses dan membentuk ulang data secara dinamis.
Comprehension penting untuk tugas seperti:
Mari kita lihat cara membuat dictionary comprehension di bawah ini:
Jika Anda ingin mendalami lebih jauh, saya sarankan mempelajari tutorial Python Dictionary Comprehension kami.
dict.fromkeys()
Metode dictionary Python lain yang berguna untuk inisialisasi adalah
dict.fromkeys(). Metode ini membuat dictionary baru dengan key yang ditentukan dan satu nilai yang sama. Ini sering digunakan untuk menginisialisasi pencacah atau flag status.Saat menggunakan
.fromkeys()dengan objek mutable seperti list atau dictionary, semua key akan mereferensikan objek yang sama di memori. Ini menciptakan jebakan “shared reference” yang dapat menimbulkan perilaku tak terduga. Mari lihat contohnya:Kita bisa melihat nilai yang sama dibagikan oleh semua key pada kasus pertama. Untuk menghindarinya, kita perlu menggunakan dictionary comprehension untuk list yang independen.
Metode Dictionary Python untuk Akses dan Modifikasi
Setelah dictionary dibuat, berinteraksi dengan data di dalamnya adalah salah satu tugas pemrograman harian yang paling umum. Mari lihat beberapa caranya.
Mengakses dan mengambil nilai
Ada beberapa cara berbeda untuk mengakses nilai.
Notasi bracket
Cara paling langsung untuk mengambil nilai dari dictionary adalah melalui notasi bracket
d[key], yang mengembalikan nilai terkait jika key ada. Pendekatan ini ideal saat Anda yakin key ada di dictionary. Berikut kodenya:Metode .get()
Untuk pengambilan yang lebih aman ketika keberadaan key tidak pasti, metode
.get()memberikan solusi elegan. Metode ini mengembalikanNone(atau nilai default yang ditentukan) alih-alih melempar pengecualian jika key tidak ada.Metode .setdefault()
Metode
.setdefault()menggabungkan pengambilan dan penyisipan dalam satu operasi. Ia mengambil nilai jika key ada, atau menyisipkan nilai default dan mengembalikannya jika key tidak ada—sempurna untuk pola akumulasi.Memodifikasi dictionary
Dictionary bersifat dinamis; Anda sering perlu menambah atau menghapus data seiring program berjalan.
Menambahkan pasangan key-value dengan metode .update()
Menambahkan satu pasangan semudah assignment (
d['new'] = 1), tetapi untuk operasi massal, metode.update()lebih baik. Metode ini menerima dictionary lain atau iterable pasangan key-value, dan menggabungkannya ke objek yang ada.Mari lihat cara menggunakan metode
.update():Untuk panduan terperinci tentang menambahkan item, saya sarankan membaca panduan Python Dictionary Append ini.
Menghapus item dari dictionary
Python menawarkan tiga metode berbeda untuk menghapus entri dictionary, masing-masing dengan perilaku dan kasus penggunaan berbeda:
.pop(key): Menghapus key dan mengembalikan nilainya. Berguna saat Anda perlu menggunakan data tersebut untuk terakhir kalinya sebelum menghapusnya..popitem(): Menghapus dan mengembalikan pasangan key-value terakhir yang disisipkan (LIFO). Ini adalah manfaat langsung dari sifat terurut dictionary modern.del d[key]: murni menghapus key. Tidak mengembalikan nilai dan sedikit lebih cepat jika nilai kembalian tidak diperlukan.Mari lihat contoh metode-metode ini:
Metode
.pop():Metode
.popitem():del:Mengosongkan dictionary dengan metode .clear()
Metode
.clear()mengosongkan seluruh dictionary, menyisakan objek{}kosong. Ini berbeda dari menghapus variabel itu sendiri. Objek tetap ada di memori, hanya saja kosong. Mari lihat cara kerjanya:Perbedaan di antara metode-metode ini penting. Gunakan
.pop()saat Anda memerlukan nilai yang dihapus,.popitem()untuk perilaku seperti stack,deluntuk penghapusan sederhana, dan.clear()untuk mereset dictionary sambil mempertahankan identitasnya.View dan iterasi
Pada versi lama Python 2, metode seperti
.keys()mengembalikan list statis. Di Python versi kini (3.x), metode ini mengembalikan objek view. View adalah jendela dinamis ke dictionary. Jika dictionary berubah, view mencerminkan perubahan itu seketika tanpa perlu dipanggil ulang.Iterasi melalui .keys(), .values(), dan .items()
Anda dapat beriterasi melalui key (
.keys()), nilai (.values()), atau keduanya sekaligus menggunakan.items(). Mari lihat metode-metode ini dengan contoh:Sifat dinamis objek view berarti ia secara otomatis mencerminkan perubahan yang dibuat pada dictionary setelah view dibuat. Mari lihat contohnya:
Irisan dan gabungan
Fitur kuat dari objek view adalah mendukung operasi himpunan. Anda dapat melakukannya langsung pada key view untuk membandingkan dua dictionary secara efisien menggunakan operator berikut:
Intersection:
&Union:
|Difference:
-Symmetric difference/XOR:
^Mari lihat contohnya:
Operasi himpunan berbasis view ini jauh lebih hemat memori daripada mengonversi dictionary ke set secara eksplisit, terutama saat bekerja dengan dataset besar.
Menghindari Error dalam Metode Dictionary Python
Karena dictionary sering digunakan sebagai antarmuka utama untuk data eksternal, seperti payload JSON dari API atau file konfigurasi, dictionary merupakan sumber umum error runtime. Menulis kode yang tangguh membutuhkan lebih dari sekadar mengetahui cara mengakses data. Ini juga butuh pengetahuan cara menangani ketiadaannya.
Error dictionary paling umum adalah
KeyError, yang terjadi saat mencoba mengakses key yang tidak ada. Python menawarkan dua pendekatan filosofis untuk menanganinya:EAFP: Mengelola KeyError dan pengecualian
Pola EAFP dianggap lebih Pythonic dan sering berkinerja lebih baik saat key biasanya ada, karena menghindari pengecekan berulang. Pola ini menggunakan blok
try-exceptuntuk menangani error setelah terjadi, dengan asumsi operasi biasanya akan berhasil. Mari lihat cara kerjanya:Namun, ada skenario di mana gagal secara jelas, membiarkan
KeyErrormenyebar, justru lebih disukai daripada kegagalan senyap. Dalam pendekatan LBYL, Anda secara eksplisit memeriksa keberadaan key sebelum mengaksesnya. Mari lihat contohnya:Memanggil fungsi ini akan memunculkan
KeyErrorjika ada key yang hilang di dictionary, dan itu adalah perilaku yang benar karena lebih baik gagal saat inisialisasi daripada diam-diam gagal saat produksi.Penanganan error eksplisit
Saat memproses data dari sumber eksternal seperti API atau input pengguna, penanganan error eksplisit menjadi penting. Mari lihat contohnya:
Memahami kapan menggunakan
.get()versus notasi bracket versustry-exceptitu penting.LBYL: Pengujian keanggotaan secara proaktif
Pendekatan LBYL menggunakan pernyataan kondisional dengan operator
indannot inuntuk memeriksa keberadaan key sebelum mencoba mengaksesnya. Pola ini lebih jelas saat berurusan dengan logika kondisional atau ketika Anda perlu mengambil tindakan berbeda berdasarkan keberadaan key. Mari lihat contohnya:Saat memvalidasi dictionary yang berasal dari sumber eksternal seperti JSON dari API, file CSV, atau input pengguna, pengujian keanggotaan secara proaktif memberikan logika validasi yang jelas dan mudah dibaca. Mari lihat contohnya:
Memilih pendekatan yang tepat
Pilihan antara EAFP dan LBYL sering bergantung pada kasus penggunaan Anda. Gunakan EAFP saat operasi biasanya berhasil dan pengecualian jarang terjadi. Gunakan LBYL saat Anda memerlukan logika percabangan eksplisit atau saat memvalidasi input sebelum operasi mahal.
Sebelum melakukan iterasi atas dictionary yang berasal dari sumber eksternal, praktik terbaik adalah:
Dengan mengikuti praktik ini, Anda dapat membuat kode jauh lebih tangguh saat menangani data yang tidak terduga atau tidak lengkap dari API, input pengguna, atau file konfigurasi.
Untuk tinjauan lebih mendalam tentang penulisan kode Python yang tangguh, saya sarankan mengikuti kursus Writing Efficient Python Code.
Metode Dictionary Python Lanjutan
Seiring proyek data science Anda bertambah kompleks, Anda sering perlu menggabungkan, menyalin, dan mentransformasi dictionary. Mempelajari operasi lanjutan ini memungkinkan Anda memanipulasi struktur data secara efisien sambil menghindari bug halus yang dapat menggagalkan pipeline. Mari lihat beberapa metodenya.
Operator penggabungan
Python 3.9 memperkenalkan operator union yang elegan untuk menggabungkan dictionary:
|(merge operator): Membuat dictionary gabungan baru.|=(update operator): Memperbarui dictionary yang ada secara in-place.Operator ini menyediakan sintaks yang bersih dan mudah dibaca untuk menggabungkan data dari banyak sumber. Mari lihat contohnya:
Untuk Python versi sebelum 3.9, metode unpacking double-star (
**) memberikan fungsi serupa:Prioritas saat terjadi benturan
Di semua teknik penggabungan, aturannya sederhana: yang terakhir menang. Nilai dari dictionary sisi kanan menimpa yang kiri jika key bertabrakan:
Perilaku ini konsisten apakah Anda menggunakan
|,|=, unpacking, atau metode.update(). Urutan itu penting, menjadikannya sangat berguna untuk manajemen konfigurasi berlapis (mis., default sistem < konfigurasi lingkungan < override pengguna).Mekanisme penyalinan
Salah satu jebakan paling berbahaya adalah kesalahpahaman tentang cara Python menangani assignment variabel.
Assignment referensi membuat alias, bukan salinan
dict_a = dict_btidak membuat salinan. Itu membuat referensi (alias). Memodifikasi yang satu akan memodifikasi yang lain. Contoh berikut mengilustrasikan konsepnya:Seperti terlihat, hanya ada satu dictionary dan baik
originalmaupunaliasmereferensikannya. Karenanya, saat nilai untuk keyrecordsdisetel menjadi2000, perubahan hanya berlaku pada satu dictionary ini.Shallow copy dengan .copy() atau .dict()
Shallow copy (
.copy()ataudict()) membuat objek dictionary baru, tetapi objek mutable bertingkat tetap berbagi referensi.Seperti terlihat, key level-atas dimodifikasi sebagaimana mestinya, namun jika Anda memodifikasi objek mutable bertingkat, itu memengaruhi baik original maupun shallow copy.
Deep copy menggunakan copy.deepcopy()
Karena itu, shallow copy sering menyebabkan bug halus pada pipeline machine learning dan manajemen konfigurasi.
Oleh karena itu, Anda memerlukan deep copy menggunakan
copy.deepcopy()dari pustaka standar untuk dictionary yang berisi objek mutable bertingkat. Ini menyalin semua objek bertingkat secara rekursif, menciptakan struktur yang sepenuhnya independen. Contoh berikut menunjukkan hal ini:Praktik terbaik penggabungan dan penyalinan
Dengan menerapkan teknik-teknik berikut, Anda akan memastikan integritas data dan keterpeliharaan di pipeline yang kompleks.
Gunakan
|dan|=untuk penggabungan dictionary yang bersih.Ingat bahwa yang terakhir dilihat akan menang saat terjadi benturan.
Bedakan antara assignment referensi, shallow copy, dan deep copy untuk menghindari bug halus.
Selalu utamakan
copy.deepcopy()saat bekerja dengan struktur bertingkat yang mutable dalam kode produksi.Tipe Dictionary Python yang Terspesialisasi
Walau
dictstandar serbaguna, pustaka standar Python menawarkan mapping terspesialisasi yang dioptimalkan untuk tugas tertentu seperti penghitungan, pengelompokan, atau penegakan struktur data. Memilih tipe terspesialisasi yang tepat dapat sangat menyederhanakan kode Anda dan mencegah satu kelas bug secara keseluruhan.Ekstensi modul collections
Modul
collectionsmenawarkan beberapa tipe dictionary terspesialisasi yang berguna.defaultdict
defaultdictdari modulcollectionsmenghilangkan pengecekan keberadaan key yang berulang dengan otomatis menginisialisasi key yang hilang dengan nilai default. Ini sangat berguna untuk tugas akumulasi seperti menghitung, mengelompokkan, atau membangun struktur bertingkat:Counter
Kelas
Counteradalah subclass dictionary terspesialisasi yang dirancang untuk menghitung objek hashable dan melakukan operasi multiset. Ini sangat kuat untuk analisis statistik dan distribusi frekuensi. Mari lihat contoh cara kerjanya:OrderedDict
Sebelum Python 3.7,
OrderedDictpenting untuk mempertahankan urutan penyisipan. Meski kini dictionary standar mempertahankan urutan,OrderedDictmasih memiliki kasus penggunaan tertentu, khususnya untuk pemeriksaan kesetaraan yang mempertimbangkan urutan dan untuk memindahkan item ke salah satu ujung:Grafik berikut mengilustrasikan kapan memilih tipe dictionary yang mana.
Keamanan tipe modern dan view hanya-baca
Seiring adopsi Python tumbuh di lingkungan produksi skala besar, keamanan tipe menjadi isu penting. Modul
typingdantypesmemperkenalkan cara untuk menegakkan struktur pada dictionary Anda. Memahamiing Python dunder methods juga dapat membantu Anda membangun objek mirip dictionary kustom dengan perilaku khusus.TypedDict
Modul
typingPython menyediakanTypedDictuntuk mendefinisikan struktur dictionary dengan key wajib tertentu dan anotasi tipe. Ini meningkatkan dokumentasi kode, mengaktifkan autocomplete di IDE, dan menangkap error tipe selama analisis statis. Mari lihat cara kerjanya:MappingProxyType
Untuk skenario ketika Anda perlu mengekspos dictionary tetapi mencegah modifikasi,
MappingProxyTypemembuat view read-only yang immutable dari dictionary standar. Ini sangat baik untuk melindungi konstanta konfigurasi global dari perubahan yang tidak disengaja. Mari lihat praktiknya:Jadi, seperti yang terlihat,
MappingProxyTypemembuat dictionary itu sendiri hanya-baca (tidak bisa menambah, menghapus, atau mengikat ulang key), tetapi tidak membekukan nilai mutable yang disimpan di dalamnya. Untuk mencegah perubahan pada struktur bertingkat, Anda juga harus membuat objek bertingkat tersebut immutable atau membungkusnya dalam view hanya-baca masing-masing.Tanda tangan fungsi dan type hint
Untuk tanda tangan fungsi dan type hint, gunakan
DictdanMappingdari modultypinguntuk mendokumentasikan struktur dictionary yang diharapkan. Kita bisa melakukannya seperti di bawah ini:Mengoptimalkan Kinerja Metode Dictionary Python
Saat dataset Anda tumbuh dari ribuan menjadi jutaan rekaman, efisiensi kode menjadi penting. Karena itu, perlu memahami karakteristik kinerja dictionary. Jadi, mari lihat kompleksitas komputasi, implikasi memori, dan strategi optimisasi untuk operasi dictionary.
Analisis kompleksitas waktu
Operasi dictionary mencapai kecepatannya melalui implementasi hash table, memberikan kompleksitas waktu O(1) rata-rata untuk tiga operasi paling umum:
get)set)delete)Performa waktu konstan ini berarti operasi-operasi tersebut memakan waktu kira-kira pada orde yang sama secara rata-rata, apakah dictionary Anda berisi 10 item atau 10 juta. Mari lihat dengan contoh kode:
Waktu lookup median di atas dapat berbeda bagi Anda bergantung pada sumber daya komputasi. Poin kuncinya adalah apa pun nilainya, mereka hampir tetap sama dengan sedikit variabilitas sehingga perbedaannya dapat diabaikan, membuktikan properti O(1).
Kasus rata-rata vs terburuk
Namun, dalam skenario terburuk, kompleksitas dapat menurun menjadi O(n) ketika terjadi tabrakan hash berlebih. Tabrakan hash terjadi ketika key berbeda menghasilkan nilai hash yang sama, memaksa Python mencari melalui beberapa entri yang disimpan pada bucket hash yang sama. Mari lihat contohnya:
Sekali lagi, hasil pasti dan degradasi kinerja akan bervariasi tergantung daya komputasi Anda. Tetapi kecenderungannya sama: lookup dengan hash buruk akan memakan waktu jauh lebih lama daripada yang mencapai kompleksitas waktu O(1).
Pengubahan ukuran dictionary
Biaya tersembunyi lain adalah pengubahan ukuran. Saat dictionary tumbuh, Python secara otomatis mengubah ukuran hash table internal untuk mempertahankan kinerja. Operasi pengubahan ukuran ini memiliki biaya komputasi, karena memerlukan rehash semua key yang ada dan mendistribusikannya kembali ke array yang lebih besar.
Python menggunakan strategi faktor pertumbuhan, biasanya setidaknya menggandakan ukuran saat ambang tertentu tercapai. Memahami pola pengubahan ukuran ini membantu saat menginisialisasi dictionary besar. Jika Anda tahu perkiraan ukuran akhir, Anda dapat melakukan pra-alokasi ruang untuk menghindari beberapa operasi pengubahan ukuran.
Manajemen memori dan pengukuran ukuran
Kecepatan sering datang dengan biaya memori. Dictionary memiliki overhead memori signifikan dibanding tuple atau list karena harus menyimpan struktur hash table (indeks, hash, key, dan nilai). Mari pahami dengan contoh kode:
Menggunakan __slots__ untuk mengurangi ukuran objek
Pada contoh di atas, Anda dapat melihat dictionary menggunakan ~2x-3x lebih banyak memori untuk data yang sama. Untuk kelas yang membuat banyak instance, menggunakan
__slots__alih-alih__dict__instance dapat sangat mengurangi konsumsi memori.Secara default, Python menyimpan atribut instance dalam dictionary yang dapat diakses melalui
__dict__, tetapi__slots__menggunakan struktur berbasis array yang lebih ringkas. Mari lihat contohnya:Dictionary Python vs pandas DataFrame
Terakhir, meski dictionary sangat baik untuk akses acak, struktur ini tidak dioptimalkan untuk pemrosesan data kolumnar. Jika Anda menangani data tabular skala besar (mis., jutaan baris), bermigrasi ke pandas DataFrame adalah pilihan bijak, karena dioptimalkan untuk efisiensi memori dan kecepatan ter-vektorisasi.
Anda dapat dengan jelas melihat bahwa DataFrame mengungguli dictionary pada contoh kita. Untuk alur kerja data science yang membutuhkan pencarian cepat sekaligus operasi analitis, pertimbangkan pendekatan hibrida seperti menggunakan dictionary untuk pengindeksan dan akses cepat, lalu konversi ke DataFrame untuk analisis massal.
Kunci optimisasi adalah mencocokkan struktur data dengan pola akses Anda. Jika Anda sering melakukan pencarian berbasis key, dictionary adalah yang optimal. Untuk operasi kolumnar, pemfilteran, dan agregasi pada dataset besar, DataFrame memberikan kinerja yang baik.
Kesimpulan
Dictionary lebih dari sekadar wadah penyimpanan sederhana. Struktur ini adalah perekat yang menyatukan aplikasi data science yang kompleks. Jika Anda membangun tabel lookup cepat untuk skrip atau merancang pipeline data ber-throughput tinggi, maka dictionary kemungkinan merupakan alat paling berharga Anda.
Namun, bergantung pada perilaku default tanpa penanganan error yang tangguh adalah resep kegagalan runtime. Pola penanganan error yang kuat, seperti menggunakan pendekatan EAFP versus LBYL dan validasi proaktif, sangat mungkin mencegah kegagalan runtime saat memproses data eksternal.
Koleksi terspesialisasi seperti
defaultdict,Counter, danTypedDictmembuat kode Anda naik kelas dari fungsional menjadi siap produksi. Selalu perhatikan kompleksitas waktu dan manajemen memori untuk memastikan kode Anda berjalan efisien. Juga, ingat bahwa optimisasi adalah proses iteratif.Untuk terus membangun keterampilan Python Anda, saya sarankan mengikuti Intermediate Python untuk materi lebih lanjut tentang struktur data, atau jalur karier Python Developer untuk lintasan belajar yang komprehensif.