Python & AI Tutorials Logo
Pemrograman Python

18. Model Data dan Objek Python: Referensi, Perbandingan, dan Salinan

Memahami bagaimana Python menyimpan dan mengelola data itu krusial untuk menulis program yang benar. Di bab ini, kita akan mengeksplorasi model objek Python (object model)—sistem fundamental yang mengatur bagaimana semua data bekerja di Python. Kamu akan belajar kenapa beberapa assignment membuat salinan yang independen sementara yang lain membuat referensi yang dibagikan, cara membandingkan objek dengan benar, dan cara menghindari jebakan umum saat bekerja dengan koleksi.

Pengetahuan ini akan membantu kamu memahami perilaku mengejutkan yang mungkin pernah kamu temui, seperti kenapa memodifikasi satu list kadang memengaruhi list lain, atau kenapa membandingkan dua list dengan == memberi hasil berbeda dibanding membandingkannya dengan is.

18.1) Semua Adalah Objek di Python

Di Python, setiap potongan data adalah objek (object). Ini bukan sekadar konsep teoretis—ada implikasi praktis untuk cara programmu bekerja.

Saat kamu membuat angka, string, list, atau nilai lainnya, Python membuat sebuah objek di memori. Objek adalah wadah yang menyimpan:

  • Data sebenarnya (nilai (value))
  • Informasi tentang jenis datanya (tipe (type))
  • Pengenal unik (identitas (identity))

Mari lihat ini dalam praktik:

python
# Membuat berbagai tipe objek
number = 42
text = "Hello"
items = [1, 2, 3]
 
# Masing-masing variabel ini merujuk ke sebuah objek di memori
print(number)  # Output: 42
print(text)    # Output: Hello
print(items)   # Output: [1, 2, 3]

Bahkan nilai sederhana seperti integer adalah objek. Ini berarti mereka punya kemampuan lebih dari sekadar menyimpan angka:

python
# Integer adalah objek yang punya method
number = 42
print(number.bit_length())  # Output: 6
 
# String adalah objek yang punya method
text = "hello"
print(text.upper())  # Output: HELLO
 
# List adalah objek yang punya method
items = [3, 1, 2]
items.sort()
print(items)  # Output: [1, 2, 3]

Kenapa ini penting? Karena saat kamu melakukan assignment ke variabel atau mengoper data ke sebuah fungsi (function), kamu tidak menyalin objeknya—kamu membuat sebuah referensi (reference) ke objek yang sama. Ini secara fundamental berbeda dari cara beberapa bahasa pemrograman lain bekerja, dan memahami perbedaan ini akan mencegah banyak bug yang membingungkan.

python
# Membuat sebuah objek list
original = [1, 2, 3]
 
# Ini tidak membuat list baru - ini membuat referensi lain
# ke objek list yang SAMA
another_name = original
 
# Memodifikasi lewat satu referensi memengaruhi yang lain
another_name.append(4)
 
print(original)      # Output: [1, 2, 3, 4]
print(another_name)  # Output: [1, 2, 3, 4]

Baik original maupun another_name merujuk ke objek list yang sama di memori. Saat kita memodifikasi list lewat another_name, kita melihat perubahan lewat original karena keduanya sama-sama melihat objek yang sama.

Variabel: original

Objek List: 1, 2, 3, 4

Variabel: another_name

Perilaku ini disebut semantik referensi (reference semantics), dan ini adalah salah satu konsep terpenting dalam pemrograman Python. Kita akan membahasnya lebih dalam sepanjang bab ini.

18.2) Identitas, Tipe, dan Nilai Objek

Setiap objek di Python punya tiga karakteristik fundamental yang mendefinisikannya: identitas (identity), tipe (type), dan nilai (value). Memahami karakteristik ini membantu kamu menalar bagaimana objek berperilaku dan bagaimana membandingkannya dengan benar.

18.2.1) Identitas Objek dengan id()

Identitas sebuah objek adalah angka unik yang Python berikan saat objek dibuat. Identitas ini tidak pernah berubah selama masa hidup objek—ini seperti alamat permanen di memori.

Kamu bisa mengambil identitas objek menggunakan fungsi id():

python
# Membuat objek dan mengecek identitasnya
x = [1, 2, 3]
y = [1, 2, 3]
z = x
 
print(id(x))  # Output: 140234567890123 (contoh - angka sebenarnya bervariasi)
print(id(y))  # Output: 140234567890456 (berbeda dari x)
print(id(z))  # Output: 140234567890123 (sama seperti x)

Angka yang kamu lihat akan berbeda setiap kali kamu menjalankan program, tapi polanya tetap sama: x dan y punya identitas berbeda karena mereka objek yang berbeda, walaupun berisi nilai yang sama. Sementara itu, z punya identitas yang sama dengan x karena z hanyalah nama lain untuk objek yang sama.

Berikut contoh praktis yang menunjukkan kenapa identitas itu penting:

python
# Dua siswa dengan nilai yang sama
student1_grades = [85, 90, 92]
student2_grades = [85, 90, 92]
 
# Ini objek yang berbeda (identitas berbeda)
print(id(student1_grades))  # Output: 140234567890123 (contoh)
print(id(student2_grades))  # Output: 140234567890456 (berbeda)
 
# Memodifikasi yang satu tidak memengaruhi yang lain
student1_grades.append(88)
print(student1_grades)  # Output: [85, 90, 92, 88]
print(student2_grades)  # Output: [85, 90, 92]

Sekarang pertimbangkan skenario yang berbeda:

python
# Nilai seorang siswa dilacak oleh dua variabel
original_grades = [85, 90, 92]
backup_reference = original_grades
 
# Ini merujuk ke objek yang SAMA (identitas sama)
print(id(original_grades))    # Output: 140234567890123 (contoh)
print(id(backup_reference))   # Output: 140234567890123 (sama!)
 
# Memodifikasi lewat salah satu nama memengaruhi keduanya
backup_reference.append(88)
print(original_grades)     # Output: [85, 90, 92, 88]
print(backup_reference)    # Output: [85, 90, 92, 88]

Inti penting: Saat dua variabel punya identitas yang sama, keduanya merujuk ke objek yang persis sama di memori. Perubahan yang dibuat lewat satu variabel terlihat lewat variabel yang lain karena hanya ada satu objek yang dimodifikasi.

18.2.2) Tipe Objek dengan type()

Tipe sebuah objek menentukan jenis data apa yang disimpannya dan operasi apa yang bisa kamu lakukan padanya. Seperti yang kita pelajari di Bab 3, kamu bisa mengecek tipe objek menggunakan fungsi type():

python
# Berbagai tipe objek
number = 42
text = "Hello"
items = [1, 2, 3]
mapping = {"name": "Alice"}
 
print(type(number))   # Output: <class 'int'>
print(type(text))     # Output: <class 'str'>
print(type(items))    # Output: <class 'list'>
print(type(mapping))  # Output: <class 'dict'>

Tipe sebuah objek tidak pernah berubah setelah dibuat. Kamu tidak bisa mengubah integer menjadi string—kamu hanya bisa membuat objek string baru berdasarkan nilai integer tersebut:

python
# Tipe ditetapkan saat pembuatan
x = 42
print(type(x))  # Output: <class 'int'>
 
# Ini tidak mengubah tipe x - ini membuat objek string BARU
# dan membuat x merujuk ke objek baru itu
x = str(x)
# Objek integer asli (42) masih ada di memori sampai garbage collected
# x sekarang menunjuk ke objek yang benar-benar berbeda: string "42"
 
print(type(x))  # Output: <class 'str'>
print(x)        # Output: 42 (sekarang string, bukan integer)

Memahami tipe itu krusial karena tipe yang berbeda mendukung operasi yang berbeda:

python
# List mendukung append
grades = [85, 90]
grades.append(92)
print(grades)  # Output: [85, 90, 92]
 
# String tidak punya append - mereka immutable
text = "Hello"
# text.append(" World")  # AttributeError: 'str' object has no attribute 'append'
 
# Tapi string mendukung concatenation
text = text + " World"
print(text)  # Output: Hello World

18.2.3) Nilai Objek

Nilai sebuah objek adalah data sebenarnya yang dikandungnya. Berbeda dengan identitas dan tipe, nilai dapat berubah untuk objek mutable (seperti list dan dictionary) tetapi tidak dapat berubah untuk objek immutable (seperti integer dan string).

python
# Untuk objek mutable, nilainya bisa berubah
shopping_cart = ["milk", "bread"]
print(shopping_cart)  # Output: ['milk', 'bread']
 
shopping_cart.append("eggs")
print(shopping_cart)  # Output: ['milk', 'bread', 'eggs']
# Objek yang sama (identitas sama), nilai berbeda
 
# Untuk objek immutable, nilainya tidak bisa berubah
count = 5
print(count)  # Output: 5
 
count = count + 1
print(count)  # Output: 6
# Ini membuat objek BARU dengan identitas baru

Berikut contoh lengkap yang menunjukkan ketiga karakteristik:

python
# Membuat sebuah objek list
data = [10, 20, 30]
 
print("Identity:", id(data))      # Output: Identity: 140234567890123 (contoh)
print("Type:", type(data))        # Output: Type: <class 'list'>
print("Value:", data)             # Output: Value: [10, 20, 30]
 
# Memodifikasi nilai (identitas dan tipe tetap sama)
data.append(40)
 
print("Identity:", id(data))      # Output: Identity: 140234567890123 (tidak berubah)
print("Type:", type(data))        # Output: Type: <class 'list'> (tidak berubah)
print("Value:", data)             # Output: Value: [10, 20, 30, 40] (berubah)

Objek

Identitas: ID Unik

Tipe: class 'list'

Nilai: 10, 20, 30, 40

Tidak pernah berubah

Tidak pernah berubah

Bisa berubah untuk tipe mutable

Memahami tiga karakteristik ini membantu kamu memprediksi bagaimana objek akan berperilaku di programmu. Identitas memberi tahu apakah dua variabel merujuk ke objek yang sama, tipe memberi tahu operasi apa yang diizinkan, dan nilai memberi tahu data apa yang saat ini dipegang objek tersebut.

18.3) Tipe Mutable dan Immutable

Salah satu perbedaan terpenting di Python adalah antara tipe mutable (dapat diubah) dan immutable (tidak dapat diubah). Perbedaan ini memengaruhi bagaimana objek berperilaku saat kamu mencoba mengubahnya, dan memahaminya mencegah banyak kesalahan pemrograman yang umum.

18.3.1) Tipe Immutable: Nilai yang Tidak Bisa Berubah

Objek immutable adalah objek yang nilainya tidak bisa diubah setelah dibuat. Saat kamu melakukan operasi yang terlihat seperti memodifikasi objek immutable, Python sebenarnya membuat objek baru dengan nilai yang sudah dimodifikasi.

Tipe immutable di Python mencakup:

  • Integer (int)
  • Bilangan floating-point (float)
  • String (str)
  • Tuple (tuple)
  • Boolean (bool)
  • None (NoneType)

Mari lihat immutability beraksi dengan integer:

python
# Membuat sebuah integer
x = 100
print("Original x:", x)           # Output: Original x: 100
print("Identity of x:", id(x))    # Output: Identity of x: 140234567890123 (contoh)
 
# Ini terlihat seperti kita memodifikasi x, tapi sebenarnya kita membuat objek baru
x = x + 1
print("Modified x:", x)           # Output: Modified x: 101
print("Identity of x:", id(x))    # Output: Identity of x: 140234567890456 (berbeda!)

Identitas berubah karena x = x + 1 membuat objek integer yang benar-benar baru dengan nilai 101. Objek asli bernilai 100 masih ada (sampai garbage collector Python menghapusnya), tapi x sekarang merujuk ke objek yang berbeda.

String menunjukkan immutability dengan lebih jelas:

python
# Membuat sebuah string
message = "Hello"
print("Original:", message)        # Output: Original: Hello
print("Identity:", id(message))    # Output: Identity: 140234567890789 (contoh)
 
# Method string tidak memodifikasi yang asli - mereka mengembalikan string baru
uppercase = message.upper()
print("Original:", message)        # Output: Original: Hello (tidak berubah)
print("Uppercase:", uppercase)     # Output: Uppercase: HELLO
print("Identity of original:", id(message))    # Output: Identity of original: 140234567890789 (sama)
print("Identity of uppercase:", id(uppercase)) # Output: Identity of uppercase: 140234567891012 (berbeda)

Bahkan operasi yang terlihat seperti memodifikasi string sebenarnya membuat objek string baru:

python
# Membangun string dengan concatenation
text = "Python"
print("Before:", text, "- ID:", id(text))  # Output: Before: Python - ID: 140234567891234 (contoh)
 
text = text + " Programming"
print("After:", text, "- ID:", id(text))   # Output: After: Python Programming - ID: 140234567891567 (berbeda)

Kenapa immutability penting: Objek immutable aman untuk dibagikan (share) di antara bagian-bagian program yang berbeda karena tidak ada bagian yang bisa secara tidak sengaja memodifikasinya. Ini membuat kode kamu lebih bisa diprediksi dan lebih mudah dipahami.

18.3.2) Tipe Mutable: Nilai yang Bisa Berubah

Objek mutable adalah objek yang nilainya bisa diubah setelah dibuat tanpa membuat objek baru. Identitas objek tetap sama, tetapi isinya bisa dimodifikasi.

Tipe mutable di Python mencakup:

  • List (list)
  • Dictionary (dict)
  • Set (set)

Mari lihat mutability dengan list:

python
# Membuat sebuah list
numbers = [1, 2, 3]
print("Original:", numbers)        # Output: Original: [1, 2, 3]
print("Identity:", id(numbers))    # Output: Identity: 140234567892345 (contoh)
 
# Memodifikasi list - objek yang sama, nilai berbeda
numbers.append(4)
print("Modified:", numbers)        # Output: Modified: [1, 2, 3, 4]
print("Identity:", id(numbers))    # Output: Identity: 140234567892345 (sama!)

Identitas tidak berubah karena kita memodifikasi objek list yang sudah ada alih-alih membuat yang baru. Ini secara fundamental berbeda dari cara tipe immutable bekerja.

Dictionary dan set juga mutable:

python
# Contoh dictionary
student = {"name": "Alice", "grade": 85}
print("Before:", student, "- ID:", id(student))  # Output: Before: {'name': 'Alice', 'grade': 85} - ID: 140234567893012 (contoh)
 
student["grade"] = 90  # Memodifikasi dictionary
print("After:", student, "- ID:", id(student))   # Output: After: {'name': 'Alice', 'grade': 90} - ID: 140234567893012 (sama)
 
# Contoh set
unique_numbers = {1, 2, 3}
print("Before:", unique_numbers, "- ID:", id(unique_numbers))  # Output: Before: {1, 2, 3} - ID: 140234567893345 (contoh)
 
unique_numbers.add(4)  # Memodifikasi set
print("After:", unique_numbers, "- ID:", id(unique_numbers))   # Output: After: {1, 2, 3, 4} - ID: 140234567893345 (sama)

18.3.3) Kenapa Mutability Penting dalam Praktik

Perbedaan antara tipe mutable dan immutable menjadi krusial saat beberapa variabel merujuk ke objek yang sama:

python
# Contoh immutable - berbagi yang aman
x = "Hello"
y = x  # y merujuk ke objek string yang sama
 
# "Memodifikasi" x membuat objek baru
x = x + " World"
 
print(x)  # Output: Hello World
print(y)  # Output: Hello (tidak berubah - y masih merujuk ke yang asli)
python
# Contoh mutable - modifikasi bersama
list1 = [1, 2, 3]
list2 = list1  # list2 merujuk ke objek list yang SAMA
 
# Memodifikasi lewat list1 memengaruhi list2
list1.append(4)
 
print(list1)  # Output: [1, 2, 3, 4]
print(list2)  # Output: [1, 2, 3, 4] (juga berubah!)

Tipe Immutable

int, float, str, tuple, bool, None

Nilai tidak bisa berubah

Operasi membuat objek baru

Aman untuk dibagikan

Tipe Mutable

list, dict, set

Nilai bisa berubah

Operasi memodifikasi objek yang ada

Berbagi butuh kehati-hatian

Memahami mutability itu penting untuk:

  1. Memprediksi perilaku: Mengetahui apakah sebuah operasi membuat objek baru atau memodifikasi yang sudah ada
  2. Menghindari bug: Mencegah modifikasi yang tidak diinginkan saat objek dibagikan
  3. Menulis kode yang efisien: Memilih tipe yang tepat untuk kebutuhanmu
  4. Memahami perilaku fungsi: Mengetahui kapan parameter fungsi bisa dimodifikasi

Di bagian berikutnya, kita akan mengeksplorasi bagaimana assignment bekerja dengan tipe-tipe berbeda ini dan bagaimana membuat salinan yang independen saat dibutuhkan.

18.4) Cara Assignment Bekerja dengan Objek

Assignment di Python tidak menyalin objek—assignment membuat referensi (reference) ke objek. Memahami perbedaan ini krusial untuk menulis program yang benar, terutama saat bekerja dengan tipe mutable.

18.4.1) Assignment Membuat Referensi, Bukan Salinan

Saat kamu menulis x = y, Python tidak membuat salinan dari objek yang dirujuk oleh y. Sebaliknya, Python membuat x merujuk ke objek yang sama yang dirujuk oleh y. Kedua variabel menjadi nama untuk objek yang sama di memori.

Mari lihat ini dulu dengan objek immutable:

python
# Assignment dengan integer (immutable)
a = 100
b = a  # b sekarang merujuk ke objek integer yang sama dengan a
 
print("a:", a)           # Output: a: 100
print("b:", b)           # Output: b: 100
print("Same object?", id(a) == id(b))  # Output: Same object? True
 
# "Memodifikasi" a membuat objek baru
a = a + 1
 
print("a:", a)           # Output: a: 101
print("b:", b)           # Output: b: 100 (tidak berubah)
print("Same object?", id(a) == id(b))  # Output: Same object? False

Dengan objek immutable, perilaku ini biasanya aman karena kamu tidak bisa memodifikasi objek aslinya. Saat kamu melakukan operasi yang mengubah nilai, Python membuat objek baru.

Namun, dengan objek mutable, perilakunya sangat berbeda:

python
# Assignment dengan list (mutable)
list1 = [1, 2, 3]
list2 = list1  # list2 merujuk ke objek list yang SAMA dengan list1
 
print("list1:", list1)   # Output: list1: [1, 2, 3]
print("list2:", list2)   # Output: list2: [1, 2, 3]
print("Same object?", id(list1) == id(list2))  # Output: Same object? True
 
# Memodifikasi lewat list1 memengaruhi list2
list1.append(4)
 
print("list1:", list1)   # Output: list1: [1, 2, 3, 4]
print("list2:", list2)   # Output: list2: [1, 2, 3, 4] (juga berubah!)
print("Same object?", id(list1) == id(list2))  # Output: Same object? True

Baik list1 maupun list2 adalah nama untuk objek list yang sama. Saat kamu memodifikasi list lewat salah satu nama, kamu melihat perubahan lewat keduanya karena hanya ada satu list.

Assignment dengan Tipe Immutable

Kedua variabel merujuk ke objek yang sama pada awalnya

Operasi membuat objek baru

Variabel menjadi independen

Assignment dengan Tipe Mutable

Kedua variabel merujuk ke objek yang sama

Operasi memodifikasi objek yang dibagikan

Perubahan terlihat lewat kedua variabel

Berikut contoh praktis yang menunjukkan kenapa ini penting:

python
# Mengelola nilai siswa
alice_grades = [85, 90, 92]
backup_grades = alice_grades  # Mencoba membuat backup
 
print("Original:", alice_grades)  # Output: Original: [85, 90, 92]
print("Backup:", backup_grades)   # Output: Backup: [85, 90, 92]
 
# Menambah nilai baru
alice_grades.append(88)
 
# "Backup" ikut termodifikasi!
print("Original:", alice_grades)  # Output: Original: [85, 90, 92, 88]
print("Backup:", backup_grades)   # Output: Backup: [85, 90, 92, 88]

Ini sama sekali bukan backup—kedua variabel merujuk ke list yang sama. Untuk membuat backup yang benar, kamu perlu membuat salinan (akan kita bahas di Bagian 18.8).

18.4.2) Assignment dalam Pemanggilan Fungsi

Saat kamu mengoper argumen ke sebuah fungsi, Python menggunakan semantik referensi yang sama. Parameter menjadi nama lain untuk objek yang sama:

python
# Fungsi dengan parameter immutable
def increment(number):
    number = number + 1  # Membuat objek baru
    return number
 
value = 5
result = increment(value)
 
print("Original value:", value)    # Output: Original value: 5 (tidak berubah)
print("Returned result:", result)  # Output: Returned result: 6

Parameter number pada awalnya merujuk ke objek integer yang sama dengan value. Saat kita melakukan number = number + 1, kita membuat objek integer baru dan membuat number merujuk ke objek itu. Objek asli (dan value) tetap tidak berubah.

Dengan objek mutable, perilakunya berbeda:

python
# Fungsi dengan parameter mutable
def add_item(items, new_item):
    items.append(new_item)  # Memodifikasi list asli
 
shopping_list = ["milk", "bread"]
add_item(shopping_list, "eggs")
 
print("Original list:", shopping_list)  # Output: Original list: ['milk', 'bread', 'eggs']

Parameter items merujuk ke objek list yang sama dengan shopping_list. Saat kita memodifikasi list lewat items, kita memodifikasi list aslinya.

Berikut kesalahan umum dan cara menghindarinya:

python
# KESALAHAN: Tanpa sengaja memodifikasi yang asli
def process_grades(grades):
    grades.append(100)  # Memodifikasi yang asli!
    return grades
 
student_grades = [85, 90, 92]
processed = process_grades(student_grades)
 
print("Original:", student_grades)  # Output: Original: [85, 90, 92, 100] (termodifikasi!)
print("Processed:", processed)      # Output: Processed: [85, 90, 92, 100]
 
# BENAR: Buat salinan jika kamu tidak ingin memodifikasi yang asli
def process_grades_safely(grades):
    # Buat list baru dengan elemen yang sama
    result = grades + [100]  # Concatenation membuat list baru
    return result
 
student_grades = [85, 90, 92]
processed = process_grades_safely(student_grades)
 
print("Original:", student_grades)  # Output: Original: [85, 90, 92] (tidak berubah)
print("Processed:", processed)      # Output: Processed: [85, 90, 92, 100]

Catatan penting tentang argumen default yang mutable: Jebakan umum terkait melibatkan penggunaan objek mutable sebagai nilai parameter default (seperti def func(items=[]):). Parameter default dibuat sekali saat fungsi didefinisikan, bukan setiap kali dipanggil, yang bisa menyebabkan perilaku tak terduga di mana list default mengumpulkan nilai di banyak pemanggilan fungsi. Kita akan membahas ini secara detail di Bab 20, tapi sadari bahwa ini adalah sumber bug yang sering muncul saat bekerja dengan parameter mutable.

18.5) Semantik Referensi dan Aliasing Objek

Semantik referensi (reference semantics) berarti variabel di Python adalah nama yang merujuk ke objek, bukan wadah yang menyimpan nilai. Saat beberapa variabel merujuk ke objek yang sama, ini disebut aliasing (aliasing). Memahami aliasing itu penting untuk memprediksi bagaimana programmu berperilaku.

18.5.1) Apa Itu Aliasing?

Aliasing terjadi saat dua atau lebih variabel merujuk ke objek yang sama di memori. Variabel-variabel tersebut menjadi "alias" satu sama lain—nama yang berbeda untuk hal yang sama.

Mari lihat aliasing dengan contoh sederhana:

python
# Membuat list dan sebuah alias
original = [1, 2, 3]
alias = original  # alias merujuk ke list yang sama dengan original
 
print("Original:", original)  # Output: Original: [1, 2, 3]
print("Alias:", alias)        # Output: Alias: [1, 2, 3]
print("Same object?", id(original) == id(alias))  # Output: Same object? True
 
# Memodifikasi lewat alias
alias.append(4)
 
# Perubahan terlihat lewat kedua nama
print("Original:", original)  # Output: Original: [1, 2, 3, 4]
print("Alias:", alias)        # Output: Alias: [1, 2, 3, 4]

Hanya ada satu objek list di memori, tapi punya dua nama: original dan alias. Modifikasi apa pun yang dilakukan lewat salah satu nama memengaruhi objek dasar yang sama.

Berikut contoh yang lebih realistis dengan catatan siswa:

python
# Database siswa dengan aliasing
students = {
    "alice": {"name": "Alice", "grade": 85},
    "bob": {"name": "Bob", "grade": 90}
}
 
# Membuat alias ke catatan Alice
alice_record = students["alice"]
 
print("Alice's grade:", alice_record["grade"])  # Output: Alice's grade: 85
 
# Memodifikasi lewat alias
alice_record["grade"] = 95
 
# Perubahan terlihat di dictionary asli
print("Updated grade:", students["alice"]["grade"])  # Output: Updated grade: 95

Variabel alice_record adalah alias untuk dictionary yang disimpan pada students["alice"]. Saat kita memodifikasi alice_record, kita memodifikasi dictionary yang sama yang tersimpan di dictionary students.

18.5.2) Mendeteksi Aliasing dengan Operator is

Kamu bisa mengecek apakah dua variabel adalah alias (merujuk ke objek yang sama) menggunakan operator is:

python
# Mengecek aliasing
list1 = [1, 2, 3]
list2 = list1      # Alias
list3 = [1, 2, 3]  # Objek berbeda dengan nilai yang sama
 
print("list1 is list2:", list1 is list2)  # Output: list1 is list2: True (alias)
print("list1 is list3:", list1 is list3)  # Output: list1 is list3: False (objek berbeda)
print("list1 == list3:", list1 == list3)  # Output: list1 == list3: True (nilai sama)

Operator is mengecek identitas (apakah dua variabel merujuk ke objek yang sama), sedangkan == mengecek nilai (apakah dua objek punya isi yang sama). Kita akan membahas perbedaan ini secara detail di Bagian 18.6.

18.5.3) Aliasing dalam Koleksi

Aliasing menjadi lebih kompleks saat objek disimpan dalam koleksi:

python
# Membuat list berisi list
row = [0, 0, 0]
grid = [row, row, row]  # Ketiga elemen adalah alias untuk list yang sama!
 
print("Grid:")
for r in grid:
    print(r)
# Output:
# [0, 0, 0]
# [0, 0, 0]
# [0, 0, 0]
 
# Memodifikasi satu elemen memengaruhi semua baris
grid[0][0] = 1
 
print("\nAfter modification:")
for r in grid:
    print(r)
# Output:
# [1, 0, 0]
# [1, 0, 0]
# [1, 0, 0]

Ini adalah kesalahan umum saat mencoba membuat grid 2D. Ketiga baris adalah alias untuk list yang sama, jadi memodifikasi satu baris memodifikasi semuanya.

Cara yang benar untuk membuat baris yang independen:

python
# Membuat baris yang independen
grid = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]  # Setiap baris adalah list terpisah
 
print("Grid:")
for r in grid:
    print(r)
# Output:
# [0, 0, 0]
# [0, 0, 0]
# [0, 0, 0]
 
# Sekarang memodifikasi satu elemen hanya memengaruhi baris itu
grid[0][0] = 1
 
print("\nAfter modification:")
for r in grid:
    print(r)
# Output:
# [1, 0, 0]
# [0, 0, 0]
# [0, 0, 0]

18.6) Equality, Identity, dan Membership (==, is, dan in) di Berbagai Tipe

Python menyediakan tiga operator fundamental untuk membandingkan dan mengecek hubungan antarobjek: == untuk equality, is untuk identity, dan in untuk membership. Memahami kapan harus memakai masing-masing operator itu krusial untuk menulis program yang benar.

18.6.1) Equality dengan == (Membandingkan Nilai)

Operator == mengecek apakah dua objek punya nilai yang sama. Tidak penting apakah mereka objek yang sama di memori—yang penting hanya apakah isinya sama.

python
# Membandingkan nilai dengan ==
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1
 
print(list1 == list2)  # Output: True (nilai sama)
print(list1 == list3)  # Output: True (nilai sama)

Walaupun list1 dan list2 adalah objek yang berbeda di memori, mereka punya nilai yang sama, jadi == mengembalikan True.

Berikut cara == bekerja untuk tipe yang berbeda:

python
# Equality di berbagai tipe
print(42 == 42)              # Output: True (nilai integer sama)
print(42 == 42.0)            # Output: True (integer sama dengan float bernilai sama)
print("hello" == "hello")    # Output: True (nilai string sama)
print([1, 2] == [1, 2])      # Output: True (isi list sama)
print({"a": 1} == {"a": 1})  # Output: True (isi dictionary sama)
 
# Nilai berbeda
print(42 == 43)              # Output: False
print("hello" == "Hello")    # Output: False (case-sensitive)
print([1, 2] == [2, 1])      # Output: False (urutan penting)

Untuk koleksi, == melakukan perbandingan mendalam (deep comparison)—ia mengecek apakah semua elemen sama:

python
# Deep comparison dengan struktur bersarang
list1 = [[1, 2], [3, 4]]
list2 = [[1, 2], [3, 4]]
 
print(list1 == list2)  # Output: True (semua elemen bersarang sama)
 
# Walaupun list bagian dalam adalah objek yang berbeda
print(id(list1[0]) == id(list2[0]))  # Output: False (objek berbeda)
print(list1[0] == list2[0])          # Output: True (nilai sama)

18.6.2) Identity dengan is (Membandingkan Identitas Objek)

Operator is mengecek apakah dua variabel merujuk ke objek yang sama di memori. Ia membandingkan identitas, bukan nilai.

python
# Membandingkan identitas dengan is
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1
 
print(list1 is list2)  # Output: False (objek berbeda)
print(list1 is list3)  # Output: True (objek sama)
 
# Mengonfirmasi dengan id()
print(id(list1) == id(list2))  # Output: False
print(id(list1) == id(list3))  # Output: True

Kapan memakai is: Penggunaan is yang paling umum adalah untuk mengecek None:

python
# Mengecek None (cara yang benar)
def find_student(name, students):
    """Return student record or None if not found."""
    for student in students:
        if student["name"] == name:
            return student
    return None
 
students = [
    {"name": "Alice", "grade": 85},
    {"name": "Bob", "grade": 90}
]
 
result = find_student("Charlie", students)
 
# Gunakan 'is' untuk mengecek None
if result is None:
    print("Student not found")  # Output: Student not found
else:
    print(f"Found: {result}")

18.6.3) Membership dengan in (Mengecek Keberadaan)

Operator in mengecek apakah sebuah nilai terkandung di dalam sebuah koleksi. Ia bekerja dengan string, list, tuple, set, dan dictionary:

python
# Membership pada berbagai tipe
print(2 in [1, 2, 3])           # Output: True
print("hello" in "hello world")  # Output: True
print("x" in {"x": 10, "y": 20}) # Output: True (mengecek key)
print(5 in {1, 2, 3, 4, 5})     # Output: True

Untuk dictionary, in mengecek apakah sebuah key ada:

python
# Mengecek membership dictionary
student = {"name": "Alice", "grade": 85, "age": 20}
 
print("name" in student)    # Output: True (key ada)
print("Alice" in student)   # Output: False (value, bukan key)
print("grade" in student)   # Output: True (key ada)
 
# Mengecek value perlu mengakses .values()
print("Alice" in student.values())  # Output: True

Operator not in mengecek ketidakhadiran:

python
# Mengecek ketidakhadiran
shopping_list = ["milk", "bread", "eggs"]
 
if "butter" not in shopping_list:
    print("Don't forget to buy butter!")  # Output: Don't forget to buy butter!

Ringkasan kapan memakai masing-masing operator:

  • Gunakan == saat kamu ingin mengecek apakah dua objek punya nilai yang sama
  • Gunakan is saat kamu ingin mengecek apakah dua variabel merujuk ke objek yang sama (paling sering untuk None, atau saat debugging aliasing)
  • Gunakan in saat kamu ingin mengecek apakah sebuah nilai terkandung di dalam sebuah koleksi

Memahami perbedaan ini membantu kamu menulis perbandingan yang lebih presisi dan benar di programmu.

18.7) Membandingkan Objek yang Berisi Objek Lain

Saat objek berisi objek lain (seperti list di dalam list, atau dictionary yang berisi list), perbandingan menjadi lebih bernuansa. Memahami cara Python membandingkan struktur bersarang itu penting untuk bekerja dengan data yang kompleks.

18.7.1) Cara == Bekerja pada Struktur Bersarang

Operator == melakukan perbandingan rekursif (recursive comparison) untuk struktur bersarang. Ia membandingkan bukan hanya container luar, tetapi semua objek bersarang juga:

python
# Membandingkan list bersarang
list1 = [[1, 2], [3, 4]]
list2 = [[1, 2], [3, 4]]
 
print(list1 == list2)  # Output: True
 
# Walaupun list bagian dalam adalah objek yang berbeda
print(id(list1[0]) == id(list2[0]))  # Output: False
print(list1[0] == list2[0])          # Output: True

Python membandingkan setiap elemen secara rekursif. Agar list1 == list2 bernilai True, setiap elemen yang bersesuaian harus sama, termasuk elemen bersarang.

Berikut contoh yang lebih kompleks:

python
# Struktur bersarang dengan banyak level
data1 = {
    "students": [
        {"name": "Alice", "grades": [85, 90, 92]},
        {"name": "Bob", "grades": [88, 91, 87]}
    ],
    "class": "Python 101"
}
 
data2 = {
    "students": [
        {"name": "Alice", "grades": [85, 90, 92]},
        {"name": "Bob", "grades": [88, 91, 87]}
    ],
    "class": "Python 101"
}
 
print(data1 == data2)  # Output: True

Python membandingkan:

  1. Key dan value dictionary di level teratas ("students" dan "class")
  2. List siswa
  3. Setiap dictionary siswa (dengan key "name" dan "grades")
  4. List nilai untuk setiap siswa
  5. Setiap angka nilai individual

Semua level harus cocok agar perbandingan mengembalikan True.

18.7.2) Urutan Penting untuk Sequence

Untuk sequence (list dan tuple), urutan elemen itu penting:

python
# Urutan penting di list
list1 = [[1, 2], [3, 4]]
list2 = [[3, 4], [1, 2]]
 
print(list1 == list2)  # Output: False (urutan berbeda)
 
# Tapi urutan tidak penting untuk set
set1 = {frozenset([1, 2]), frozenset([3, 4])}
set2 = {frozenset([3, 4]), frozenset([1, 2])}
 
print(set1 == set2)  # Output: True (set tidak berurutan)

18.7.3) Membandingkan Koleksi dari Tipe Berbeda

Tipe koleksi yang berbeda (list, tuple, set) tidak pernah equal satu sama lain, walaupun mereka berisi elemen yang sama:

python
# Membandingkan tipe yang berbeda
print([1, 2, 3] == (1, 2, 3))  # Output: False (list vs tuple)
print([1, 2, 3] == {1, 2, 3})  # Output: False (list vs set)
 
# Bahkan dengan elemen yang sama
list_version = [1, 2, 3]
tuple_version = (1, 2, 3)
set_version = {1, 2, 3}
 
print(list_version == tuple_version)  # Output: False
print(list_version == set_version)    # Output: False
print(tuple_version == set_version)   # Output: False

18.8) Shallow Copy pada List, Dict, dan Set

Saat bekerja dengan objek mutable, kamu sering perlu membuat salinan yang independen untuk menghindari modifikasi yang tidak diinginkan. Misalnya, saat membuat salinan cadangan data sebelum memprosesnya, membuat skenario pengujian tanpa memengaruhi data produksi, atau mengoper data ke fungsi yang seharusnya tidak memodifikasi yang asli. Memahami bagaimana mekanisme penyalinan (copying) Python bekerja membantu kamu membuat salinan yang benar-benar independen saat dibutuhkan.

Namun, tidak semua metode penyalinan membuat salinan yang sepenuhnya independen. Memahami perbedaan antara shallow copy dan deep copy itu krusial untuk menghindari bug yang halus.

18.8.1) Apa Itu Shallow Copy?

Shallow copy membuat objek baru, tetapi tidak membuat salinan dari objek-objek yang ada di dalamnya. Sebaliknya, objek baru itu berisi referensi ke objek bersarang yang sama seperti milik objek asli.

Mari lihat ini dengan list sederhana:

python
# Membuat shallow copy dari list sederhana
original = [1, 2, 3]
copy = original.copy()  # Membuat shallow copy
 
print("Original:", original)  # Output: Original: [1, 2, 3]
print("Copy:", copy)          # Output: Copy: [1, 2, 3]
 
# Mereka objek yang berbeda
print("Same object?", original is copy)  # Output: Same object? False
 
# Memodifikasi copy tidak memengaruhi original
copy.append(4)
 
print("Original:", original)  # Output: Original: [1, 2, 3]
print("Copy:", copy)          # Output: Copy: [1, 2, 3, 4]

Untuk list sederhana yang berisi objek immutable (seperti integer), shallow copy bekerja dengan sempurna. Salinannya independen dari yang asli.

Tapi apa yang terjadi pada struktur bersarang? Mari lihat batasan shallow copy:

python
# Shallow copy dengan list bersarang
original = [[1, 2], [3, 4]]
copy = original.copy()
 
print("Original:", original)  # Output: Original: [[1, 2], [3, 4]]
print("Copy:", copy)          # Output: Copy: [[1, 2], [3, 4]]
 
# List luar adalah objek yang berbeda
print("Same outer list?", original is copy)  # Output: Same outer list? False
 
# Tapi list bersarang adalah objek yang SAMA
print("Same nested list?", original[0] is copy[0])  # Output: Same nested list? True
 
# Memodifikasi list bersarang memengaruhi keduanya
copy[0].append(99)
 
print("Original:", original)  # Output: Original: [[1, 2, 99], [3, 4]]
print("Copy:", copy)          # Output: Copy: [[1, 2, 99], [3, 4]]

List Asli

List Bersarang 1: 1, 2, 99

List Bersarang 2: 3, 4

Shallow Copy

18.8.2) Membuat Shallow Copy untuk List

Ada beberapa cara untuk membuat shallow copy dari sebuah list:

python
# Metode 1: Menggunakan method copy()
original = [[1, 2], [3, 4]]
copy1 = original.copy()
 
# Metode 2: Menggunakan slicing list
copy2 = original[:]
 
# Metode 3: Menggunakan constructor list()
copy3 = list(original)
 
# Ketiganya membuat shallow copy
print(copy1)  # Output: [[1, 2], [3, 4]]
print(copy2)  # Output: [[1, 2], [3, 4]]
print(copy3)  # Output: [[1, 2], [3, 4]]
 
# List luar berbeda
print(original is copy1)  # Output: False
print(original is copy2)  # Output: False
print(original is copy3)  # Output: False
 
# Tapi list dalam DIBAGIKAN
print(original[0] is copy1[0])  # Output: True
print(original[0] is copy2[0])  # Output: True
print(original[0] is copy3[0])  # Output: True

18.8.3) Membuat Shallow Copy untuk Dictionary

Dictionary juga mendukung shallow copy:

python
# Metode 1: Menggunakan method copy()
original = {"name": "Alice", "grade": 85}
copy1 = original.copy()
 
# Metode 2: Menggunakan constructor dict()
copy2 = dict(original)
 
# Keduanya membuat shallow copy
print(copy1)  # Output: {'name': 'Alice', 'grade': 85}
print(copy2)  # Output: {'name': 'Alice', 'grade': 85}
 
# Mereka objek yang berbeda
print(original is copy1)  # Output: False
print(original is copy2)  # Output: False
 
# Memodifikasi copy tidak memengaruhi original
copy1["grade"] = 90
 
print("Original:", original)  # Output: Original: {'name': 'Alice', 'grade': 85}
print("Copy:", copy1)         # Output: Copy: {'name': 'Alice', 'grade': 90}

Namun, dengan struktur bersarang, batasan shallow copy yang sama berlaku:

python
# Shallow copy dengan dictionary bersarang
original = {
    "name": "Alice",
    "grades": [85, 90, 92]
}
 
copy = original.copy()
 
print("Original:", original)  # Output: Original: {'name': 'Alice', 'grades': [85, 90, 92]}
print("Copy:", copy)          # Output: Copy: {'name': 'Alice', 'grades': [85, 90, 92]}
 
# Dictionary-nya adalah objek yang berbeda
print("Same dict?", original is copy)  # Output: Same dict? False
 
# Tapi list grades adalah objek yang SAMA
print("Same grades list?", original["grades"] is copy["grades"])  # Output: Same grades list? True
 
# Memodifikasi list grades memengaruhi keduanya
copy["grades"].append(88)
 
print("Original:", original)  # Output: Original: {'name': 'Alice', 'grades': [85, 90, 92, 88]}
print("Copy:", copy)          # Output: Copy: {'name': 'Alice', 'grades': [85, 90, 92, 88]}
© 2025. Primesoft Co., Ltd.
support@primesoft.ai