Pendamping Bab 19: Proyek: Aplikasi Manajemen Kontak. Gunakan nomor contoh dan latihan yang sama dengan buku.
Tujuan Belajar
- Menghubungkan input, fungsi, koleksi, dan penyimpanan.
- Menguji tambah, cari, ubah, dan hapus kontak.
- Memeriksa apakah data bertahan setelah program dijalankan ulang.
Persiapan
Gunakan Python di komputer dan folder proyek khusus. Unduh sumber lengkap serta baca petunjuk menjalankan. Gunakan nama dan nomor fiktif untuk pengujian.
Program dengan tombol Coba di IDE dapat dijalankan di browser. Untuk program lokal, unduh sumber, ekstrak ZIP, lalu ikuti README.txt.
Pembahasan
Ikuti pembahasan pada buku, lalu gunakan kegiatan dan materi pendamping berikut untuk memeriksa pemahaman.
Studi Kasus
19-01 – Menguji komponen data kontak
Budi ditambahkan lalu dihapus; nomor Ayu diperbarui. Pengujian kecil ini memeriksa model data sebelum menu dan file dihubungkan.
Coba di IDE Unduh Sumberkontak = {"Ayu": "081234"}
kontak["Budi"] = "085678"
kontak["Ayu"] = "089999"
del kontak["Budi"]
for nama, nomor in kontak.items():
print(f"{nama}: {nomor}")
Hasil yang diharapkan:
Ayu: 089999Coba ubah: Tambahkan Citra dan periksa bahwa memperbarui Ayu tidak mengubah kontak lain.
19-P01 – Aplikasi Kontak Lengkap
Dua bagian code membentuk satu file kontak.py. Jalankan menu, simpan data, lalu buka kembali untuk memeriksa penyimpanan.
Unduh SumberMasukan uji, berurutan: '1', 'Ayu', '0812345', '3', 'ayu', '4', 'Ayu', '', '0812999', '2', '7'.
import os
import json # Ditambahkan untuk opsi penyimpanan JSON
KONTAK_FILE_TXT = "data_kontak.txt"
KONTAK_FILE_JSON = "data_kontak.json"
DATA_FORMAT = "json" # Bisa diganti ke "txt"
def load_kontak():
kontak = {}
if DATA_FORMAT == "json":
file_to_load = KONTAK_FILE_JSON
else:
file_to_load = KONTAK_FILE_TXT
if not os.path.exists(file_to_load):
return kontak
try:
if DATA_FORMAT == "json":
with open(file_to_load, "r", encoding="utf-8") as file:
kontak = json.load(file)
if not isinstance(kontak, dict) or not all(
isinstance(k, str) and isinstance(v, str)
for k, v in kontak.items()
):
raise ValueError(
"Data kontak harus pasangan teks."
)
else: # Format TXT
with open(file_to_load, "r", encoding="utf-8") as file:
for baris in file:
if baris.strip(): # Pastikan baris tidak kosong
try:
baris_bersih = baris.strip()
nama, no = baris_bersih.split(",", 1)
kontak[nama] = no
except ValueError:
msg = "Peringatan: Baris "
msg += f"'{baris.strip()}'"
msg += f" dalam {file_to_load}"
msg += " tidak valid dan akan dilewati."
print(msg)
except (OSError, ValueError) as e:
print(f"Error saat memuat kontak dari {file_to_load}: {e}")
return {}
return kontak
def simpan_kontak(kontak):
if DATA_FORMAT == "json":
file_to_save = KONTAK_FILE_JSON
else:
file_to_save = KONTAK_FILE_TXT
try:
if DATA_FORMAT == "json":
with open(file_to_save, "w", encoding="utf-8") as file:
json.dump(kontak, file, indent=4)
else: # Format TXT
with open(file_to_save, "w", encoding="utf-8") as file:
for nama, no in kontak.items():
file.write(f"{nama},{no}\n")
return True
except OSError as e:
print(f"Error saat menyimpan kontak ke {file_to_save}: {e}")
return False
def tampilkan_menu():
print("\n--- Menu Aplikasi Kontak ---")
print("1. Tambah Kontak Baru")
print("2. Lihat Semua Kontak")
print("3. Cari Kontak")
print("4. Edit Kontak")
print("5. Hapus Kontak")
format_info = f"6. Ganti Format Penyimpanan "
format_info += f"(Saat ini: {DATA_FORMAT.upper()})"
print(format_info)
print("7. Keluar")
def tambah_kontak(kontak):
nama = input("Masukkan Nama Kontak: ").strip()
if not nama:
print("Nama tidak boleh kosong.")
return
if nama in kontak:
print("Kontak dengan nama tersebut sudah ada.")
print("Gunakan fitur edit jika ingin mengubah.")
return
no = input("Masukkan No HP: ").strip()
if not (
no.isascii() and no.isdecimal()
): # Validasi nomor HP harus angka
print("Nomor HP harus berupa angka.")
return
kontak[nama] = no
print(f"Kontak '{nama}' berhasil ditambahkan.")
def tampilkan_kontak(kontak):
if not kontak:
print("Belum ada kontak yang tersimpan.")
return
print("\n--- Daftar Kontak ---")
# Menggunakan enumerate untuk nomor urut yang lebih rapi
for i, (nama, no) in enumerate(kontak.items(), 1):
print(f"{i}. {nama}: {no}")
def cari_kontak(kontak):
if not kontak:
print("Belum ada kontak untuk dicari.")
return
prompt = "Masukkan nama kontak yang ingin dicari: "
nama_dicari = input(prompt).strip()
if nama_dicari in kontak:
hasil = f"Kontak ditemukan -> {nama_dicari}: "
hasil += kontak[nama_dicari]
print(hasil)
else:
# Pencarian case-insensitive sederhana
ketemu_case_insensitive = False
for nama, no in kontak.items():
if nama.lower() == nama_dicari.lower():
print(f"Kontak ditemukan -> {nama}: {no}")
ketemu_case_insensitive = True
break
if not ketemu_case_insensitive:
print(f"Kontak '{nama_dicari}' tidak ditemukan.")
def edit_kontak(kontak):
if not kontak:
print("Belum ada kontak untuk diedit.")
return
prompt = "Masukkan nama kontak yang ingin diedit: "
nama_edit = input(prompt).strip()
if nama_edit not in kontak:
print(f"Kontak '{nama_edit}' tidak ditemukan.")
return
print(f"Kontak saat ini: {nama_edit}: {kontak[nama_edit]}")
prompt_nama = f"Masukkan nama baru (kosongkan jika tidak "
prompt_nama += f"ingin mengubah nama '{nama_edit}'): "
nama_baru = input(prompt_nama).strip()
target_nama = nama_baru or nama_edit
prompt_no = f"Masukkan nomor HP baru untuk '{target_nama}' "
prompt_no += "(kosongkan jika tidak ingin mengubah): "
no_baru = input(prompt_no).strip()
if no_baru and not (no_baru.isascii() and no_baru.isdecimal()):
print("Nomor HP harus berupa angka 0-9.")
return
current_no = kontak[nama_edit] # Simpan nomor HP saat ini
# Logika untuk mengubah nama dan/atau nomor
if nama_baru and nama_baru != nama_edit:
if nama_baru in kontak: # Cek apakah nama baru sudah ada
print(f"Nama kontak '{nama_baru}' sudah ada.")
print("Tidak bisa mengubah.")
return
# Hapus entri lama, tambahkan entri baru
del kontak[nama_edit]
kontak[nama_baru] = no_baru if no_baru else current_no
print(f"Kontak '{nama_edit}' diubah menjadi '{nama_baru}'.")
elif no_baru:
kontak[nama_baru or nama_edit] = no_baru
nama_target = nama_baru or nama_edit
print(f"Nomor HP untuk '{nama_target}' berhasil diubah.")
else:
print("Tidak ada perubahan dilakukan.")
def hapus_kontak(kontak):
if not kontak:
print("Belum ada kontak untuk dihapus.")
return
prompt = "Masukkan nama kontak yang ingin dihapus: "
nama_hapus = input(prompt).strip()
if nama_hapus in kontak:
del kontak[nama_hapus] # Hapus kontak dari dictionary
print(f"Kontak '{nama_hapus}' berhasil dihapus.")
else:
print(f"Kontak '{nama_hapus}' tidak ditemukan.")
def ganti_format_penyimpanan():
global DATA_FORMAT
print(f"Format saat ini: {DATA_FORMAT.upper()}")
pilihan_format = input("Pilih format baru ('txt' atau 'json'): ")
pilihan_format = pilihan_format.lower().strip()
if pilihan_format in ["txt", "json"]:
DATA_FORMAT = pilihan_format
print(f"Format penyimpanan diubah ke: {DATA_FORMAT.upper()}")
print("Perubahan format efektif saat menyimpan berikutnya.")
print("Disarankan untuk memuat ulang data jika kamu")
print("sudah memiliki file dalam format baru atau")
print("ingin mengkonversi data lama.")
else:
print("Pilihan format tidak valid.")
def main():
kontak = load_kontak()
while True:
tampilkan_menu()
pilihan = input("Pilih menu (1-7): ").strip()
if pilihan == "1":
tambah_kontak(kontak)
elif pilihan == "2":
tampilkan_kontak(kontak)
elif pilihan == "3":
cari_kontak(kontak)
elif pilihan == "4":
edit_kontak(kontak)
elif pilihan == "5":
hapus_kontak(kontak)
elif pilihan == "6":
ganti_format_penyimpanan()
elif pilihan == "7":
if simpan_kontak(kontak):
print("Data kontak berhasil disimpan. Sampai jumpa!")
break
print(
"Data belum tersimpan. Periksa folder lalu coba lagi."
)
else:
print("Pilihan tidak valid.")
print("Silakan masukkan angka antara 1-7.")
if __name__ == "__main__":
main()
Hasil yang diharapkan:
--- Menu Aplikasi Kontak ---
1. Tambah Kontak Baru
2. Lihat Semua Kontak
3. Cari Kontak
4. Edit Kontak
5. Hapus Kontak
6. Ganti Format Penyimpanan (Saat ini: JSON)
7. Keluar
Pilih menu (1-7): Masukkan Nama Kontak: Masukkan No HP: Kontak 'Ayu' berhasil ditambahkan.
--- Menu Aplikasi Kontak ---
1. Tambah Kontak Baru
2. Lihat Semua Kontak
3. Cari Kontak
4. Edit Kontak
5. Hapus Kontak
6. Ganti Format Penyimpanan (Saat ini: JSON)
7. Keluar
Pilih menu (1-7): Masukkan nama kontak yang ingin dicari: Kontak ditemukan -> Ayu: 0812345
--- Menu Aplikasi Kontak ---
1. Tambah Kontak Baru
2. Lihat Semua Kontak
3. Cari Kontak
4. Edit Kontak
5. Hapus Kontak
6. Ganti Format Penyimpanan (Saat ini: JSON)
7. Keluar
Pilih menu (1-7): Masukkan nama kontak yang ingin diedit: Kontak saat ini: Ayu: 0812345
Masukkan nama baru (kosongkan jika tidak ingin mengubah nama 'Ayu'): Masukkan nomor HP baru untuk 'Ayu' (kosongkan jika tidak ingin mengubah): Nomor HP untuk 'Ayu' berhasil diubah.
--- Menu Aplikasi Kontak ---
1. Tambah Kontak Baru
2. Lihat Semua Kontak
3. Cari Kontak
4. Edit Kontak
5. Hapus Kontak
6. Ganti Format Penyimpanan (Saat ini: JSON)
7. Keluar
Pilih menu (1-7):
--- Daftar Kontak ---
1. Ayu: 0812999
--- Menu Aplikasi Kontak ---
1. Tambah Kontak Baru
2. Lihat Semua Kontak
3. Cari Kontak
4. Edit Kontak
5. Hapus Kontak
6. Ganti Format Penyimpanan (Saat ini: JSON)
7. Keluar
Pilih menu (1-7): Data kontak berhasil disimpan. Sampai jumpa!
Rangkuman
Proyek menggabungkan fungsi dengan dictionary, validasi, penyimpanan, dan penanganan error. Keberhasilan tidak cukup dinilai dari menu yang muncul: periksa perubahan data dan hasil setelah program dibuka kembali.
Cek Pemahaman
Lima pertanyaan ini sama dengan pertanyaan di buku. Periksa jawaban di kuis untuk melihat skor dan pembahasan.
- Mengapa nomor telepon disimpan sebagai string?
- Pengujian penyimpanan yang tepat adalah …
- Apa yang perlu diperiksa saat menghapus kontak?
- Mengapa fitur dipisahkan ke fungsi?
- Skenario penting saat memuat data adalah …
Latihan Mandiri
Latihan 19-L01 – Menghitung kontak
Dasar. Buat fungsi jumlah_kontak(data) dan uji pada dictionary berisi Ayu dan Budi.
Kerjakan di IDE Unduh LatihanHasil yang diharapkan:
2
Lihat Petunjuk
Jumlah pasangan dictionary sama dengan jumlah kontak pada model ini.
Lihat Solusi
def jumlah_kontak(data):
return len(data)
kontak = {"Ayu": "081234", "Budi": "085678"}
print(jumlah_kontak(kontak))
Fungsi dapat diuji sebelum dihubungkan ke menu.
Coba Solusi di IDEUnduh SolusiLatihan 19-L02 – Pencarian tanpa membedakan kapital
Penerapan. Cari ayu pada dictionary dengan kunci Ayu. Cetak nomor jika ditemukan.
Kerjakan di IDE Unduh LatihanHasil yang diharapkan:
081234
Lihat Petunjuk
Gunakan casefold pada kedua teks.
Lihat Solusi
kontak = {"Ayu": "081234", "Budi": "085678"}
kata = "ayu"
for nama, nomor in kontak.items():
if nama.casefold() == kata.casefold():
print(nomor)
break
else:
print("Tidak ditemukan")
Normalisasi dilakukan untuk perbandingan, sedangkan nama asli tetap tersimpan.
Coba Solusi di IDEUnduh SolusiLatihan 19-L03 – Simpan dan muat JSON
Pengembangan. Simpan dua kontak latihan ke kontak_uji.json, baca kembali, lalu cetak jumlah dan nomor Ayu.
Unduh LatihanHasil yang diharapkan:
2
081234
Lihat Petunjuk
Gunakan dump dan load untuk objek file.
Lihat Solusi
import json
kontak = {"Ayu": "081234", "Budi": "085678"}
with open("kontak_uji.json", "w", encoding="utf-8") as f:
json.dump(kontak, f, ensure_ascii=False, indent=2)
with open("kontak_uji.json", encoding="utf-8") as f:
hasil = json.load(f)
print(len(hasil))
print(hasil["Ayu"])
Nol awal tetap ada karena nomor disimpan sebagai string.
Unduh SolusiPengembangan
Kegiatan Tambahan
Proyek ini adalah dasar yang bagus. Berikut beberapa ide untuk mengembangkannya lebih jauh:
- Validasi Nomor HP Lebih Ketat: Gunakan ekspresi
reguler (modul
re) untuk memvalidasi format nomor HP yang lebih spesifik (misalnya, harus diawali08dan memiliki panjang tertentu). - Tambah Field Data: Izinkan pengguna menyimpan informasi tambahan seperti email, alamat, tanggal lahir, atau catatan untuk setiap kontak. Ini akan memerlukan perubahan struktur data (mungkin dictionary di dalam dictionary) dan penyesuaian pada fungsi simpan/muat, terutama jika menggunakan JSON.
- Pengurutan Kontak: Tambahkan opsi untuk menampilkan kontak yang diurutkan berdasarkan nama (A-Z atau Z-A).
- Backup dan Restore: Buat fitur untuk mencadangkan data kontak ke file lain dan memulihkannya.
- Antarmuka Pengguna Grafis (GUI): Untuk pengalaman yang lebih interaktif, coba bangun GUI menggunakan pustaka seperti Tkinter (bawaan Python), PyQt, atau Kivy.
- Pencarian Lebih Lanjut: Izinkan pencarian berdasarkan nomor HP atau field lainnya.
- Konversi Data: Jika pengguna mengganti format penyimpanan, tawarkan untuk mengkonversi data dari format lama ke format baru secara otomatis.
- Grup Kontak: Tambahkan kemampuan untuk mengelompokkan kontak (misalnya, Keluarga, Teman, Kerja).
- Logging: Catat aktivitas pengguna (misalnya, kapan kontak ditambahkan atau dihapus) ke file log terpisah.
Kegiatan Tambahan
Untuk menguji pemahamanmu dan mengasah keterampilan, coba kerjakan latihan berikut:
- Implementasikan Fitur “Hitung Jumlah Kontak”:
- Tambahkan opsi menu baru (misalnya, nomor 7, sehingga “Keluar” menjadi nomor 8).
- Buat fungsi baru, misalnya
hitung_total_kontak(kontak), yang menerima dictionary kontak dan mencetak jumlah total kontak yang tersimpan. - Panggil fungsi ini dari
main()ketika pengguna memilih opsi menu tersebut.
- Tambahkan Field Email:
- Modifikasi aplikasi agar setiap kontak juga bisa menyimpan alamat email (opsional).
- Saat menambah atau mengedit kontak, minta input email. Jika pengguna
tidak memasukkan email, simpan string kosong atau
None. - Ubah cara data disimpan dan dimuat agar menyertakan email. Jika menggunakan format TXT, pikirkan pemisah yang aman (mungkin lebih baik fokus pada JSON untuk fitur ini).
- Tampilkan email saat melihat detail kontak atau semua kontak.
- Pencarian Berdasarkan Nomor HP:
- Modifikasi fungsi
cari_kontakatau buat fungsi barucari_kontak_via_nomoryang memungkinkan pengguna mencari kontak berdasarkan nomor HP mereka.
- Modifikasi fungsi
- Konfirmasi Sebelum Menghapus:
- Dalam fungsi
hapus_kontak, sebelum benar-benar menghapus, tanyakan konfirmasi kepada pengguna (misalnya, “Kamu yakin ingin menghapus [nama_kontak]? (y/n)”). Lanjutkan penghapusan hanya jika pengguna menjawab ‘y’.
- Dalam fungsi
- (Tantangan) Konversi Data Otomatis Saat
Ganti Format:
- Modifikasi fungsi
ganti_format_penyimpanan. Setelah format diubah, jika file data dalam format lama ada, coba muat data dari format lama tersebut, lalu langsung simpan dalam format baru yang dipilih. Berikan pesan kepada pengguna mengenai proses ini. - Contoh: Jika format saat ini TXT dan ada
data_kontak.txt, lalu pengguna ganti ke JSON. Aplikasi harus membacadata_kontak.txt, lalu menyimpan isinya kedata_kontak.json.
- Modifikasi fungsi
Proyek mini ini menunjukkan bagaimana berbagai konsep Python yang telah kamu pelajari dapat digabungkan untuk membuat aplikasi yang fungsional. Dengan terus berlatih dan bereksperimen, kamu akan menjadi semakin mahir dalam memecahkan masalah menggunakan Python.
Referensi dan Materi Pendamping
- Dictionary di Python: Pasangan Key-Value
- items() di Python: Pasangan Kunci-Nilai Dictionary
- Membaca File di Python: open() dan with
- try except di Python: Menangani Error
Langkah Selanjutnya
Bab 20 membahas Proyek: Game, Kalkulator, dan Chatbot. Sebelum melanjutkan, pastikan kamu dapat menjelaskan tujuan belajar dan menyelesaikan tiga latihan inti bab ini.
Edisi September 2026. Nomor contoh, pertanyaan, dan latihan digunakan bersama pada buku dan halaman ini.
