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.

python
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:

text
[1, 2, 3, 4]
True

Setelah 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.

python
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:

text
[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.

python
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:

text
asli  : [[1, 2, 99], [3, 4]]
salinan: [[1, 2, 99], ['diganti']]

Dua baris mutasi di atas menghasilkan dua perilaku berbeda:

  1. salinan[0].append(99): menembus satu lapis — salinan[0] dan asli[0] adalah objek list yang sama, sehingga angka 99 muncul di keduanya.
  2. salinan[1] = ["diganti"]: hanya mengganti slot milik salinan di list terluar — penggantian tidak pernah menyentuh asli[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.

python
import copy

asli = [[1, 2], [3, 4]]
salinan = copy.deepcopy(asli)

salinan[0].append(99)

print("asli  :", asli)
print("salinan:", salinan)

Output yang Dihasilkan:

text
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:

SituasiAlat yang tepat
Hanya butuh nama lain untuk objek yang samab = a
List datar atau elemen immutable (integer, string, tuple)shallow copy: a.copy(), list(a), a[:]
Struktur bersarang berisi list/dictionary mutablecopy.deepcopy(a)