Phân biệt giữa '==' và 'is'
Trong Python, hai toán tử phổ biến để so sánh đối tượng là `'=='` và `'is'`. `'=='` kiểm tra xem giá trị của các đối tượng có bằng nhau không, còn `'is'` kiểm tra xem chúng có cùng một địa chỉ bộ nhớ hay không.
x = 10
y = 10
print(x == y) # True
print(id(x)) # Ví dụ: 139876543210987
print(id(y)) # Ví dụ: 139876543210987
print(x is y) # True
Python tối ưu hóa việc lưu trữ cho các số nguyên từ -5 đến 256 bằng cách tái sử dụng các tham chiếu. Vì vậy, `x is y` trả về `True` khi `x` và `y` nằm trong khoảng này.
Tuy nhiên, trong thực tế, `'=='` được dùng nhiều hơn vì nó tập trung vào giá trị của đối tượng. Khi cần xác định một đối tượng đơn lẻ (singleton), như `None`, `'is'` thường được dùng:
if x is None:
pass
if x is not None:
pass
Toán tử `'is'` nhanh hơn `'=='` vì `'is'` không thể bị ghi đè, trong khi `'=='` gọi phương thức `__eq__()` có thể phức tạp hơn.
Sao chép nông và sâu
Sao chép nông có thể thực hiện qua các cách sau:
1. Sử dụng constructor của kiểu dữ liệu.
2. Dùng toán tử cắt lát với các dãy.
3. Sử dụng hàm `copy.copy()`.
lst1 = [1, 2, 3]
lst2 = list(lst1)
print(lst1 == lst2) # True
print(lst1 is lst2) # False
st1 = {1, 2, 3}
st2 = set(st1)
print(st1 == st2) # True
print(st1 is st2) # False
lst1 = [1, 2, 3]
lst2 = lst1[:]
print(lst1 == lst2) # True
print(lst1 is lst2) # False
import copy
lst1 = [1, 2, 3]
lst2 = copy.copy(lst1)
Với tuple, việc sao chép nông sẽ tạo ra một tham chiếu mới, không phải bản sao thật sự:
tp1 = (1, 2, 3)
tp2 = tuple(tp1)
print(tp1 == tp2) # True
print(tp1 is tp2) # True
Sao chép nông giữ lại các tham chiếu đến các phần tử gốc, dẫn đến những vấn đề nếu các phần tử đó là các đối tượng có thể thay đổi.
lst1 = [[1, 2], (30, 40)]
lst2 = list(lst1)
lst1.append(100)
lst1[0].append(3)
print(lst1) # [[1, 2, 3], (30, 40), 100]
print(lst2) # [[1, 2, 3], (30, 40)]
lst1[1] += (50, 60)
print(lst1) # [[1, 2, 3], (30, 40, 50, 60), 100]
print(lst2) # [[1, 2, 3], (30, 40)]
Sao chép sâu (`deepcopy`) tạo ra các bản sao hoàn toàn độc lập:
import copy
lst1 = [[1, 2], (30, 40)]
lst2 = copy.deepcopy(lst1)
lst1.append(100)
lst1[0].append(3)
print(lst1) # [[1, 2, 3], (30, 40), 100]
print(lst2) # [[1, 2], (30, 40)]
Tuy nhiên, sao chép sâu cũng có hạn chế, đặc biệt khi đối tượng chứa tham chiếu vòng lặp.
import copy
a = [1]
a.append(a)
print(a) # [1, [...]]
b = copy.deepcopy(a)
print(b) # [1, [...]]
Để tránh lỗi vô tận, `deepcopy` duy trì một từ điển theo dõi các đối tượng đã sao chép.