Không có câu trả lời nào ở đây cung cấp cho bạn bất kỳ mã nào để làm việc để thực sự minh họa tại sao điều này xảy ra ở vùng đất Python. Và điều này thật thú vị khi nhìn vào một cách tiếp cận sâu sắc hơn nên ở đây đi.
Lý do chính khiến điều này không hoạt động như bạn mong đợi là vì trong Python, khi bạn viết:
i += 1
nó không làm những gì bạn nghĩ nó đang làm Số nguyên là bất biến. Điều này có thể được nhìn thấy khi bạn nhìn vào những gì đối tượng thực sự có trong Python:
a = 0
print('ID of the first integer:', id(a))
a += 1
print('ID of the first integer +=1:', id(a))
Hàm id đại diện cho một giá trị duy nhất và không đổi cho một đối tượng trong suốt vòng đời của nó. Về mặt khái niệm, nó ánh xạ lỏng lẻo đến một địa chỉ bộ nhớ trong C / C ++. Chạy đoạn mã trên:
ID of the first integer: 140444342529056
ID of the first integer +=1: 140444342529088
Điều này có nghĩa là cái đầu tiên a
không còn giống với cái thứ hai a
, bởi vì id của chúng khác nhau. Có hiệu quả họ đang ở các vị trí khác nhau trong bộ nhớ.
Với một đối tượng, tuy nhiên, mọi thứ hoạt động khác nhau. Tôi đã ghi đè +=
toán tử ở đây:
class CustomInt:
def __iadd__(self, other):
# Override += 1 for this class
self.value = self.value + other.value
return self
def __init__(self, v):
self.value = v
ints = []
for i in range(5):
int = CustomInt(i)
print('ID={}, value={}'.format(id(int), i))
ints.append(int)
for i in ints:
i += CustomInt(i.value)
print("######")
for i in ints:
print('ID={}, value={}'.format(id(i), i.value))
Chạy kết quả này trong đầu ra sau:
ID=140444284275400, value=0
ID=140444284275120, value=1
ID=140444284275064, value=2
ID=140444284310752, value=3
ID=140444284310864, value=4
######
ID=140444284275400, value=0
ID=140444284275120, value=2
ID=140444284275064, value=4
ID=140444284310752, value=6
ID=140444284310864, value=8
Lưu ý rằng thuộc tính id trong trường hợp này thực sự giống nhau cho cả hai lần lặp, mặc dù giá trị của đối tượng là khác nhau (bạn cũng có thể tìm thấy id
giá trị int mà đối tượng giữ, sẽ thay đổi khi nó đang thay đổi - bởi vì số nguyên là bất biến).
So sánh điều này với khi bạn chạy cùng một bài tập với một đối tượng bất biến:
ints_primitives = []
for i in range(5):
int = i
ints_primitives.append(int)
print('ID={}, value={}'.format(id(int), i))
print("######")
for i in ints_primitives:
i += 1
print('ID={}, value={}'.format(id(int), i))
print("######")
for i in ints_primitives:
print('ID={}, value={}'.format(id(i), i))
Kết quả này:
ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
######
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
ID=140023258889408, value=5
######
ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
Một vài điều ở đây cần chú ý. Đầu tiên, trong vòng lặp với +=
, bạn không còn thêm vào đối tượng ban đầu. Trong trường hợp này, vì ints nằm trong số các loại bất biến trong Python , python sử dụng một id khác. Cũng thú vị lưu ý rằng Python sử dụng cùng một cơ sở id
cho nhiều biến có cùng giá trị bất biến:
a = 1999
b = 1999
c = 1999
print('id a:', id(a))
print('id b:', id(b))
print('id c:', id(c))
id a: 139846953372048
id b: 139846953372048
id c: 139846953372048
tl; dr - Python có một số loại không thay đổi, gây ra hành vi bạn nhìn thấy. Đối với tất cả các loại đột biến, kỳ vọng của bạn là chính xác.
i
không thay đổi hoặc bạn đang thực hiện một hoạt động không đột biến. Với một danh sách lồng nhaufor i in a: a.append(1)
sẽ có hành vi khác nhau; Python không sao chép các danh sách lồng nhau. Tuy nhiên, số nguyên là bất biến và phép cộng trả về một đối tượng mới, nó không thay đổi đối tượng cũ.