Serialisasi dan pickle

Serialisasi objek dengan pickle beserta catatan keamanannya.

Daftar isi

Serialisasi (serialization) adalah proses mengubah objek di dalam memori menjadi rangkaian byte yang dapat disimpan ke berkas atau dikirim antarproses, lalu dibangun kembali menjadi objek yang setara. Modul pickle adalah format serialisasi bawaan Python yang mampu menangani hampir seluruh tipe objek Python secara utuh. Topik ini membahas pemakaian pickle pada string byte dan berkas, batasannya dibandingkan JSON, serta catatan keamanan yang menyertainya.

Serialisasi dengan dumps dan loads#

Fungsi pickle.dumps() mengubah objek menjadi nilai bertipe bytes, dan fungsi pickle.loads() menguraikan byte tersebut kembali menjadi objek dengan struktur yang sama. Pasangan ini sering disebut roundtrip, karena objek keluar dan masuk kembali tanpa kehilangan bentuknya. Prosesnya berlaku pada struktur bersarang apa pun, termasuk dictionary yang berisi list di dalamnya.

python Contoh: dumps dan loads
import pickle

jadwal = {"Senin": ["Matematika", "Fisika"], "Selasa": ["Kimia", "Sejarah"]}
data = pickle.dumps(jadwal)
print(type(data).__name__)
hasil = pickle.loads(data)
print(hasil == jadwal)
print(hasil["Senin"][1])
text Keluaran
bytes
True
Fisika

Hasil dumps() berupa byte yang tidak dimaksudkan untuk dibaca manusia, dan pembandingan hasil == jadwal mengembalikan True karena isinya setara meskipun keduanya objek yang berbeda di memori. Keunggulan pickle terletak pada keutuhan tipe, yaitu dictionary tetap dictionary, kuncinya tetap bertipe aslinya, dan list di dalamnya tetap list. Untuk menyimpan dan membaca kembali, bentuk pickle.dump() dan pickle.load() bekerja langsung pada objek berkas tanpa melewati variabel byte terlebih dahulu.

Menyimpan objek ke berkas#

Berkas pickle ditulis dengan mode "wb" dan dibaca dengan mode "rb", karena isinya memang byte dan bukan teks. Akhiran .pkl yang lazim ditemui hanyalah konvensi penamaan, dan Python tidak menuntut akhiran tertentu. Pola pemakaiannya mirip penulisan berkas teks dengan open(), dengan perbedaan wajib pada pemilihan mode binernya.

python simpan-jadwal.py
import pickle
from pathlib import Path

with open("jadwal.pkl", "wb") as f:
    pickle.dump({"Rabu": ["Biologi", "Olahraga"]}, f)
with open("jadwal.pkl", "rb") as f:
    hasil = pickle.load(f)
print(hasil["Rabu"])
print("berkas tersimpan:", Path("jadwal.pkl").stat().st_size > 0)
text Keluaran
['Biologi', 'Olahraga']
berkas tersimpan: True

Pemanggilan pickle.dump() menulis seluruh objek ke berkas dalam satu langkah, dan pickle.load() membacanya kembali menjadi objek yang utuh. Ukuran berkas diperiksa melalui stat().st_size sebagai konfirmasi sederhana bahwa berkas benar-benar berisi data. Pola ini tepat untuk cache antarproses, penyimpanan keadaan program, dan data sementara yang hanya dibaca kembali oleh Python.

Batasan pickle dan perbandingan dengan JSON#

Format pickle bersifat khusus Python, sedangkan JSON adalah format teks netral yang dapat dibaca hampir semua bahasa pemrograman. Perbedaan paling terlihat ada pada tipe kunci dictionary, karena JSON menuntut kunci berupa string, sedangkan pickle mempertahankan tipe aslinya. Perbedaan lain ada pada keutuhan tipe, dan contoh berikut membandingkan keduanya pada dictionary berkunci integer.

python Contoh: kunci integer pada JSON dan pickle
import json, pickle

data = {1: "satu", 2: "dua"}
kembali = json.loads(json.dumps(data))
print(type(next(iter(kembali))).__name__)
hasil = pickle.loads(pickle.dumps(data))
print(type(next(iter(hasil))).__name__)
text Keluaran
str
int

JSON mengubah kunci 1 menjadi string '1', sedangkan pickle mengembalikannya sebagai integer seperti semula. Namun demikian, tidak semua objek dapat diserialisasi. Objek yang terikat pada keadaan luar seperti lambda, berkas yang sedang terbuka, dan koneksi jaringan ditolak oleh pickle karena tidak ada cara yang bermakna untuk membangunnya kembali.

python Contoh: objek yang tidak dapat diserialisasi
import pickle

try:
    pickle.dumps(lambda x: x + 1)
except Exception as e:
    print(type(e).__name__)
text Keluaran
PicklingError

Sebagai pedoman praktis, JSON dipilih ketika data perlu dibaca sistem lain atau disimpan dalam jangka panjang, sedangkan pickle dipilih untuk data sementara yang masa pakainya seumur proyek Python yang sama. Pilihan ini mengikuti sifat keduanya, yaitu pickle utuh tetapi terikat pada Python, dan JSON terbatas tetapi netral.

Catatan keamanan pickle#

Proses pickle.loads() tidak sekadar membaca data, melainkan menjalankan instruksi untuk membangun objek apa pun yang tercantum di dalam byte-nya, termasuk memanggil fungsi dan metode tertentu. Berkas pickle dari sumber yang tidak dipercaya dengan demikian setara dengan program yang siap dijalankan, dan memuatnya berarti memberi izin kepada penulisnya untuk mengeksekusi kode pada mesin yang menjalankannya. Sifat inilah yang membedakan pickle dari JSON, karena penguraian JSON tidak pernah menjalankan kode apa pun.

Praktik yang aman adalah membatasi pickle untuk data yang dibuat dan dibaca kembali oleh program sendiri pada lingkungan yang dikuasai. Apabila berkas pickle harus bertahan lama, kehati-hatian tambahan diperlukan, karena perubahan definisi kelas antarversi dapat membuat objek lama tidak lagi cocok dengan kelas yang ada saat dibaca. Kasus semacam ini menjadi alasan tambahan untuk memilih format teks netral pada penyimpanan jangka panjang.

Kesalahan umum#

Kesalahan pertama adalah membuka berkas pickle dengan mode teks "r" alih-alih "rb". Byte biner ditafsirkan sebagai teks, dan proses pembacaannya berakhir dengan error dekode sebelum pickle sempat bekerja.

python Contoh: mode teks pada berkas pickle
import pickle

with open("jadwal.pkl", "rb") as f:
    print(pickle.load(f)["Rabu"])
try:
    with open("jadwal.pkl", "r", encoding="utf-8") as f:
        pickle.load(f)
except UnicodeDecodeError as e:
    print(type(e).__name__)
text Keluaran
['Biologi', 'Olahraga']
UnicodeDecodeError

Kesalahan kedua adalah memakai pickle untuk data yang harus dibaca di luar Python. Berkasnya tidak dapat diuraikan oleh alat lain, sehingga aplikasi spreadsheet maupun bahasa pemrograman yang berbeda menolaknya. Kesalahan ketiga adalah menyimpan pickle sebagai arsip jangka panjang untuk objek berbasis kelas. Apabila definisi kelasnya berubah, berkas lama dapat gagal dimuat atau menghasilkan objek yang tidak lagi sesuai, sehingga arsip semacam ini lebih aman disimpan sebagai CSV atau JSON yang strukturnya eksplisit.

Ringkasan#

  • pickle.dumps() dan pickle.loads() mengonversi objek menjadi bytes dan sebaliknya, sedangkan pickle.dump() dan pickle.load() bekerja langsung pada berkas mode biner.
  • Format pickle khusus Python dan mempertahankan tipe aslinya, sedangkan JSON bersifat teks netral dengan cakupan tipe yang lebih terbatas.
  • Objek yang terikat pada keadaan luar, seperti lambda dan berkas terbuka, ditolak dengan PicklingError.
  • pickle.loads() dapat menjalankan kode arbitrer, sehingga berkas pickle hanya dimuat dari sumber yang dipercaya.

Referensi#