Belajar dengan membaca secara berurutan

Tipe Mutable dan Immutable

Pahami perbedaan tipe mutable dan immutable di Python serta jebakan referensi bersama yang sering memicu bug.

Kenapa kamu perlu mempelajari ini

Artikel ini bukan untuk pemula total, tapi topik ini tidak bisa dilewatkan jika kamu ingin memahami programming secara mendalam. Cerita klasik: pemula kena bug mutability dan menghabiskan berjam-jam untuk debug.

Di Python, kamu mungkin mengira y = x hanya menghubungkan dua variabel dengan tanda sama dengan, lalu menemukan bahwa mengedit salah satunya juga menimpa yang lain. Itulah mutability.

Bab ini menjernihkan perbedaan antara "mutable" (bisa diubah) dan "immutable" (tidak bisa diubah), dan menunjukkan cara menyalin dengan aman.

Klasifikasi tipe — masing-masing masuk ke sisi mana
Immutableint, floatstr, bool, tupleSatu berubah,lain tetapMutablelist, dictsetSatu berubah,keduanya ikutmeliputisifatmeliputisifat

Tipe yang memungkinkanmu menulis ulang variabelnya sendiri (lewat append, assignment elemen, update, dan sejenisnya) disebut mutable; yang tidak bisa disebut immutable.

list / dict / set adalah mutable, dan sisanya (int / float / str / bool / tuple) adalah immutable.

Tipe immutable — mengubah salah satu tidak memengaruhi yang lain

Dengan tipe immutable (tipe yang tidak bisa diubah), setelah kamu mengoper nilainya lewat y = x, mengubah x setelahnya tidak berdampak pada y.

Misalnya, setelah x += 1, x jadi 11, tapi y tetap pada nilai semula 10.

Alur assignment untuk tipe immutable
x = 10y = xx += 1x = 11y = 10jalankanhasil
# int bersifat immutable
x = 10
y = x
x += 1
print(x)   # 11
print(y)   # 10  <- tidak berubah

# str juga immutable
x = "hello"
y = x
x = x + " world"
print(x)   # hello world
print(y)   # hello

# tuple juga immutable
x = ("a", "b")
y = x
x = ("c", "d")
print(x)   # ('c', 'd')
print(y)   # ('a', 'b')

Dengan tipe immutable, setelah kamu menyimpan nilai ke variabel lain, memperbarui variabel aslinya tidak memengaruhi salinannya.

① Salin harga produk price = 1000 ke variabel lain old_price, lalu naikkan harganya dengan price = price + 500. Cetak price dan old_price dengan print().

② Salin tuple tag tags = ("sale", "new") ke old_tags, lalu rebind tags ke tuple lain dengan tags = ("limited", "gift"). Cetak tags dan old_tags.

(Kalau dua bagiannya benar, penjelasan akan muncul.)

Python Editor

Jalankan kode untuk melihat output

Variabel Python adalah "label nama yang menempel di kotak"

Sebuah variabel Python tidak menyimpan nilainya sendiri — ia lebih seperti label nama yang menunjuk ke data (sebuah kotak) di memori. Saat kamu menulis y = x, Python tidak membuat kotak baru; ia hanya menempelkan label nama lain (y) ke kotak yang sama yang ditunjuk x.

- Tipe immutable: rebinding seperti x = 11 hanya memindahkan label nama x ke kotak yang berbeda. y masih menunjuk ke kotak semula, jadi nilainya tetap independen.

- Tipe mutable: operasi in-place seperti x.append(...) mengubah kotak bersama itu sendiri, jadi perubahannya terlihat juga lewat y.

Inilah garis yang memisahkan immutable dari mutable.

Tipe mutable — dengan y = x, mengubah satu berarti mengubah yang lain

Sebaliknya, dengan tipe mutable (list / dict / set), menulis y = x membuat y dan x menunjuk ke kotak yang sama.

Jika kemudian kamu melakukan sesuatu yang menulis ulang isinya, misalnya x.append(...), perubahannya juga terlihat dari y.

Alur assignment untuk tipe mutable — berbagi kotak yang sama
x=[a,b]y=xx.append(c)x=[a,b,c]y=[a,b,c]jalankankeduanya

y = x tidak membuat salinan terpisah — kedua nama menunjuk ke tempat yang sama.

Operasi yang menulis ulang isinya seperti append mengubah kotak bersama yang ditunjuk oleh kedua nama, sehingga perubahannya juga terlihat dari y.

# list bersifat mutable
x = ["a", "b"]
y = x          # hanya menambahkan label y pada list yang sama

x.append("c")  # menulis ulang isinya secara langsung
print(x)       # ['a', 'b', 'c']
print(y)       # ['a', 'b', 'c']  <- y juga bertambah!

# remove juga berperilaku sama
x.remove("b")
print(y)       # ['a', 'c']  <- hilang juga dari y

# dict dan set menunjukkan perilaku yang sama
d = {"k": 1}
e = d
e["new"] = 99
print(d)       # {'k': 1, 'new': 99}  <- d juga bertambah

Kenapa berbagi adalah perilaku default?

Kalau y = x menyalin seluruh isinya setiap kali, maka ketika, misalnya, x berisi jutaan record yang diambil dari database, memori dan waktu eksekusi akan membengkak cepat.

Makanya di Python, nama variabel bukan nilainya itu sendiri — melainkan label yang menunjuk ke lokasi di memori.

Kamu tidak bisa mengubah mekanisme ini, jadi kamu (yang menulis code) yang harus berhati-hati memakainya.

Mari kita lihat pembagian yang ditimbulkan tipe mutable.

① Buat keranjang belanja cart = ["susu", "roti"], lalu tulis old_cart = cart (dengan niat menjadikannya salinan).

② Dengan pikiran bahwa kamu hanya menambah "telur" ke cart lewat cart.append("telur"), cetak cart dan old_cart dengan print().

Keduanya seharusnya berisi "telur". Itulah jebakannya hari ini.

Python Editor

Jalankan kode untuk melihat output

Gunakan copy() untuk mendapatkan versi yang independen

Ketika kamu ingin menjaga data aslinya dan membuat versi terpisah, pakai metode copy().

Menulis y = x.copy() menyalin isinya ke kotak baru lalu menyerahkannya ke y, sehingga perubahan pada x setelahnya tidak memengaruhi y.

copy() membuat "kotak yang berbeda"
x=[a,b]y=x.copy()x.append(c)x=[a,b,c]y=[a,b]jalankanindependen

Pada saat y = x.copy(), wilayah memori baru dialokasikan.

Setelah itu, menulis ulang isi x dengan x.append(...) atau sejenisnya tidak berdampak sama sekali pada y.

# list / dict / set semuanya punya .copy()
x = ["apple", "lemon"]
y = x.copy()

x.append("grape")
print(x)   # ['apple', 'lemon', 'grape']
print(y)   # ['apple', 'lemon']  <- tidak terpengaruh

# dict.copy() bekerja sama
d = {"a": 1, "b": 2}
e = d.copy()
d["c"] = 3
print(d)   # {'a': 1, 'b': 2, 'c': 3}
print(e)   # {'a': 1, 'b': 2}  <- tidak terpengaruh

# set.copy() bekerja sama
s = {1, 2}
t = s.copy()
s.add(3)
print(s)   # {1, 2, 3}
print(t)   # {1, 2}            <- tidak terpengaruh

# list punya beberapa cara lain untuk disalin
x = ["apple", "lemon"]
y1 = list(x)   # lewat constructor juga menghasilkan list baru
y2 = x[:]      # salin seluruhnya lewat slice (dari awal sampai akhir)

Hati-hati kalau list berisi list lain

copy() hanya membuat kotak luar yang baru. Nilai mutable yang ada di dalamnya (seperti list di dalam list) tetap dibagi bersama. Ini disebut shallow copy (salinan dangkal).

Hati-hati saat menangani data bersarang.

Mari perbaiki bug di bagian sebelumnya dengan copy().

Buat cart = ["susu", "roti"], tapi kali ini tulis old_cart = cart.copy(). Jalankan cart.append("telur"), lalu cetak cart dan old_cart. Kali ini, "telur" seharusnya tidak masuk ke old_cart.

Python Editor

Jalankan kode untuk melihat output

Di artikel ini kamu belajar perbedaan antara tipe mutable dan immutable dan bagaimana copy() memberimu versi yang independen.

Gagasan bahwa nama variabel bukan nilainya itu sendiri melainkan label yang menunjuk ke lokasi di memori juga berlaku di bahasa lain, bukan hanya Python. Kalau kamu bekerja dengan tipe mutable, selipkan copy().

QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Kelompok mana yang isinya hanya tipe mutable?

Soal 2Berapa nilai b setelah menjalankan code berikut?

``
a = [1, 2, 3]
b = a
a.append(4)

Soal 3Kamu ingin menjaga isi list asli a dan mendapatkan versi independen b. Cara menulis yang paling tepat yang mana?