Skenario paling membingungkan bagi pemula Python: variabel b dibuat “duplikat” dari list a, lalu b diubah — dan tiba-tiba a ikut berubah. Akar masalahnya bukan bug, melainkan cara Python mengikat variabel ke objek. Setiap penugasan hanya menyalin referensi ke objek, bukan objeknya sendiri. Chapter ini membedah tiga tingkatan penyalinan — assignment, shallow copy, deep copy — dan kapan masing-masing diperlukan.
Assignment Bukan Menyalin #
Baris b = a tidak menggandakan list. Interpreter Python cukup mengarahkan variabel b ke objek yang sama dengan yang ditunjuk a, sehingga kedua nama itu adalah dua pintu menuju satu isi.
a = [1, 2, 3]
b = a # bukan salinan: dua nama menunjuk objek list yang sama
b.append(4)
print(a)
print(a is b)Output yang Dihasilkan:
[1, 2, 3, 4]
TrueSetelah b.append(4), isi a ikut memanjang karena keduanya menunjuk objek identik — operator is mengonfirmasi hal tersebut dengan hasil True. Materi lengkap mengenai hubungan variabel, objek, dan referensi tersedia pada Chapter Memahami Object dan Reference.
Shallow Copy dengan .copy(), list(), dan Slicing #
Untuk membuat objek list baru yang benar-benar terpisah, Python menyediakan tiga cara setara: method .copy(), konstruktor list(), dan slicing penuh [:]. Ketiganya menghasilkan shallow copy — list terluar baru, tetapi setiap elemennya masih dibagi dengan list asli.
a = [1, 2, 3]
b = a.copy()
c = list(a)
d = a[:]
b.append(4)
print(a)
print(b)
print(a is b)
print(c)
print(d)Output yang Dihasilkan:
[1, 2, 3]
[1, 2, 3, 4]
False
[1, 2, 3]
[1, 2, 3]Perubahan pada b kali ini tidak menjalar: a tetap [1, 2, 3] dan a is b bernilai False. Ketiga variabel c dan d pun menunjukkan hasil serupa karena list(a) dan a[:] sama-sama membangun list terluar baru.
Jebakan Shallow Copy pada List Bersarang #
Untuk list yang elemennya berupa objek mutable — misalnya list bersarang — shallow copy tidak lagi cukup. List terluar memang baru, tetapi list di dalamnya masih ditunjuk bersama oleh kedua struktur.
asli = [[1, 2], [3, 4]]
salinan = asli.copy() # shallow copy
salinan[0].append(99) # mutasi list di dalam (masih dibagi)
salinan[1] = ["diganti"] # mengganti elemen level pertama (tidak dibagi)
print("asli :", asli)
print("salinan:", salinan)Output yang Dihasilkan:
asli : [[1, 2, 99], [3, 4]]
salinan: [[1, 2, 99], ['diganti']]Dua baris mutasi di atas menghasilkan dua perilaku berbeda:
salinan[0].append(99): menembus satu lapis —salinan[0]danasli[0]adalah objek list yang sama, sehingga angka99muncul di keduanya.salinan[1] = ["diganti"]: hanya mengganti slot miliksalinandi list terluar — penggantian tidak pernah menyentuhasli[1]yang tetap[3, 4].
Deep Copy dengan copy.deepcopy #
Solusi untuk struktur bersarang adalah fungsi deepcopy() dari module copy. Fungsi tersebut menelusuri seluruh struktur secara rekursif dan menggandakan setiap objek di setiap tingkat, sehingga hasil salinannya hidup sepenuhnya terpisah.
import copy
asli = [[1, 2], [3, 4]]
salinan = copy.deepcopy(asli)
salinan[0].append(99)
print("asli :", asli)
print("salinan:", salinan)Output yang Dihasilkan:
asli : [[1, 2], [3, 4]]
salinan: [[1, 2, 99], [3, 4]]Mutasi salinan[0].append(99) kini hanya terlihat pada salinan; list asli benar-benar bersih karena deepcopy() juga menggandakan list [1, 2] di dalamnya sebagai objek terpisah.
Pemilihan ketiganya kini bisa dipetakan dengan sederhana:
| Situasi | Alat yang tepat |
|---|---|
| Hanya butuh nama lain untuk objek yang sama | b = a |
| List datar atau elemen immutable (integer, string, tuple) | shallow copy: a.copy(), list(a), a[:] |
| Struktur bersarang berisi list/dictionary mutable | copy.deepcopy(a) |