Python gán nhiều biến cho cùng một giá trị? liệt kê hành vi


131

Tôi đã cố gắng sử dụng nhiều phép gán như hiển thị bên dưới để khởi tạo các biến, nhưng tôi đã bị nhầm lẫn bởi hành vi, tôi hy vọng sẽ gán lại danh sách giá trị một cách riêng biệt, ý tôi là b [0] và c [0] bằng 0 như trước đây.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

Kết quả là: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Đúng không? Tôi nên sử dụng gì cho nhiều bài tập? Có gì khác với cái này?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

kết quả: ('f:', 3) ('e:', 4)


2
Bạn có muốn a, bc,cho tất cả các điểm đến cùng giá trị (trong trường hợp này một danh sách), hay bạn muốn a=0, b=3c=5. Trong trường hợp đó, bạn muốn a,b,c = [0,3,5]hoặc chỉ a,b,c = 0,3,5.
chepner

Câu trả lời:


271

Nếu bạn đến với Python từ một ngôn ngữ trong C / Java / etc. gia đình, nó có thể giúp bạn ngừng suy nghĩ anhư một "biến số" và bắt đầu nghĩ về nó như một "cái tên".

a, bc không có các biến khác nhau có giá trị bằng nhau; chúng là những tên khác nhau cho cùng một giá trị. Các biến có các loại, danh tính, địa chỉ và tất cả các loại công cụ như thế.

Tên không có bất kỳ điều đó. Tất nhiên, các giá trị và bạn có thể có rất nhiều tên cho cùng một giá trị.

Nếu bạn cho Notorious B.I.G.một con chó nóng, * Biggie SmallsChris Wallacecó một con chó nóng. Nếu bạn thay đổi phần tử đầu tiên athành 1, các phần tử đầu tiên của bclà 1.

Nếu bạn muốn biết nếu hai tên được đặt tên cùng một đối tượng, hãy sử dụng istoán tử:

>>> a=b=c=[0,3,5]
>>> a is b
True

Sau đó bạn hỏi:

Có gì khác với cái này?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Ở đây, bạn đang đảo ngược tên ethành giá trị 4. Điều đó không ảnh hưởng đến tên df theo bất kỳ cách nào.

Trong phiên bản trước của bạn, bạn đã được chỉ định a[0], không phải a. Vì vậy, từ quan điểm của a[0], bạn đang phản xạ a[0], nhưng từ quan điểm của a, bạn đang thay đổi nó tại chỗ.

Bạn có thể sử dụng idhàm, cung cấp cho bạn một số số duy nhất đại diện cho danh tính của một đối tượng, để xem chính xác đối tượng đó là gì ngay cả khi iskhông thể giúp đỡ:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Lưu ý rằng a[0]đã thay đổi từ 4297261120 thành 4297261216, giờ đây nó là một tên cho một giá trị khác. Và b[0]bây giờ cũng là một tên cho cùng một giá trị mới. Đó là bởi vì abvẫn đang đặt tên cho cùng một đối tượng.


Dưới vỏ bọc, a[0]=1thực sự đang gọi một phương thức trên đối tượng danh sách. (Nó tương đương với a.__setitem__(0, 1).) Vì vậy, nó không thực sự phản đối bất cứ điều gì cả. Nó giống như gọi điện thoại my_object.set_something(1). Chắc chắn, có khả năng đối tượng đang đóng lại một thuộc tính thể hiện để thực hiện phương thức này, nhưng đó không phải là điều quan trọng; Điều quan trọng là bạn không chỉ định bất cứ điều gì, bạn chỉ đang biến đổi đối tượng. Và nó cũng tương tự với a[0]=1.


người dùng570826 hỏi:

Nếu chúng ta có, a = b = c = 10

Đó chính xác là tình huống tương tự như a = b = c = [1, 2, 3]: bạn có ba tên cho cùng một giá trị.

Nhưng trong trường hợp này, giá trị là một intints là bất biến. Trong cả hai trường hợp, bạn có thể đặt lại athành một giá trị khác (ví dụ a = "Now I'm a string!":), nhưng sẽ không ảnh hưởng đến giá trị ban đầu, bcvẫn sẽ là tên cho. Sự khác biệt là với một danh sách, bạn có thể thay đổi giá trị [1, 2, 3]thành [1, 2, 3, 4]bằng cách thực hiện, ví dụ : a.append(4); vì đó thực sự thay đổi giá trị bclà tên cho, bbây giờ sẽ b [1, 2, 3, 4]. Không có cách nào để thay đổi giá trị 10thành bất cứ điều gì khác. 10là 10 mãi mãi, giống như Claudia, ma cà rồng là 5 mãi mãi (ít nhất là cho đến khi cô được thay thế bởi Kirsten Dunst).


* Cảnh báo: Đừng cho Hot dog Notorious BIG. Gangsta rap zombie không bao giờ nên được cho ăn sau nửa đêm.


Điều gì xảy ra nếu chúng ta have, a = b = c = 10;và khi chúng ta cố gắng cập nhật giá trị của b, nó có ảnh hưởng gì khác không? mặc dù tôi đã kiểm tra id của họ giống nhau.
AJ

@ user570826: 10là bất biến, điều đó có nghĩa là không có cách nào để cập nhật giá trị, vì vậy câu hỏi của bạn không có ý nghĩa. Bạn có thể trỏ bđến một giá trị khác, nhưng làm như vậy không có tác dụng gì ac, vẫn đang chỉ vào giá trị ban đầu. Sự khác biệt mà danh sách tạo ra là chúng có thể thay đổi được, ví dụ, bạn có thể appendvào danh sách hoặc lst[0] = 3, và sẽ cập nhật giá trị, sẽ hiển thị thông qua tất cả các tên cho giá trị đó.
abarnert

72

Ho ho

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

10
IMHO, điều này thực sự trả lời câu hỏi quan trọng đầu tiên của OP về việc tôi nên sử dụng gì cho nhiều bài tập, trong khi câu trả lời cao hơn và nhiều câu hỏi hơn ở trên thì không.
Will Croxford

2
Hoặc a,b,c = 1,2,3không có dấu ngoặc hoạt động trong Python 2 hoặc 3, nếu bạn thực sự muốn có thêm cm dễ đọc.
Will Croxford

14

Vâng, đó là hành vi dự kiến. a, b và c đều được đặt làm nhãn cho cùng một danh sách. Nếu bạn muốn ba danh sách khác nhau, bạn cần chỉ định chúng riêng lẻ. Bạn có thể lặp lại danh sách rõ ràng hoặc sử dụng một trong nhiều cách để sao chép danh sách:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Các câu lệnh gán trong Python không sao chép các đối tượng - chúng liên kết tên với một đối tượng và một đối tượng có thể có nhiều nhãn như bạn đặt. Trong lần chỉnh sửa đầu tiên, thay đổi [0], bạn đang cập nhật một yếu tố của danh sách duy nhất mà a, b và c đều đề cập đến. Trong lần thứ hai, thay đổi e, bạn đang chuyển e thành nhãn cho một đối tượng khác (4 thay vì 3).


13

Trong python, mọi thứ đều là một đối tượng, cũng là các kiểu biến "đơn giản" (int, float, v.v.).

Khi bạn thay đổi một giá trị biến, bạn thực sự thay đổi con trỏ của nó và nếu bạn so sánh giữa hai biến thì nó sẽ so sánh các con trỏ của chúng . (Để rõ ràng, con trỏ là địa chỉ trong bộ nhớ máy tính vật lý nơi lưu trữ một biến).

Kết quả là, khi bạn thay đổi một giá trị biến bên trong, bạn sẽ thay đổi giá trị của nó trong bộ nhớ và nó ảnh hưởng đến tất cả các biến trỏ đến địa chỉ này.

Ví dụ của bạn, khi bạn làm:

a = b =  5 

Điều này có nghĩa là a và b trỏ đến cùng một địa chỉ trong bộ nhớ chứa giá trị 5, nhưng khi bạn thực hiện:

a = 6

Nó không ảnh hưởng đến b vì hiện tại a đang trỏ đến một vị trí bộ nhớ khác chứa 6 và b vẫn trỏ đến địa chỉ bộ nhớ chứa 5.

Nhưng, khi bạn làm:

a = b = [1,2,3]

a và b, một lần nữa, trỏ đến cùng một vị trí nhưng điểm khác biệt là nếu bạn thay đổi một trong các giá trị danh sách:

a[0] = 2

Nó thay đổi giá trị của bộ nhớ mà a là điểm, nhưng a vẫn trỏ đến cùng địa chỉ với b và kết quả là b cũng thay đổi.


6
Điều này rất dễ gây hiểu lầm. Con trỏ chắc chắn không thể nhìn thấy ở cấp độ Python và ít nhất hai trong số bốn triển khai chính (PyPy và Jython) không sử dụng chúng ngay cả khi triển khai.
abarnert

1
Bạn hoan nghênh đọc và khám phá nội bộ của python và bạn sẽ khám phá ra rằng mọi biến trong python thực sự là con trỏ.
Ori Seri

4
Không. Trong một lần triển khai Python (CPython), mỗi biến là một con trỏ tới a PyObject. Điều đó không đúng trong các triển khai khác như PyPy hoặc Jython. (Trên thực tế, thậm chí còn không rõ làm thế nào nó thể đúng, bởi vì các ngôn ngữ mà các triển khai được viết bằng thậm chí không có con trỏ.)
abarnert

Tôi nghĩ rằng việc sử dụng "con trỏ" theo nghĩa khái niệm là ổn (có lẽ với từ chối trách nhiệm rằng việc triển khai có thể thay đổi), đặc biệt nếu mục tiêu là truyền đạt hành vi.
Levon

@abarnert Khi mọi người nói Python họ có nghĩa là CPython, không phải các triển khai hiếm khi được sử dụng. Giống như khi mọi người nói Kleenex, họ có nghĩa là mô mặt. Chơi trò chơi ngữ nghĩa trong những bình luận này là thực sự không cần thiết. Đối với những gì anh ta viết, là hành vi của những gì anh ta mô tả sai?
đánh vào

10

Bạn có thể sử dụng id(name)để kiểm tra xem hai tên đại diện cho cùng một đối tượng:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Danh sách có thể thay đổi; nó có nghĩa là bạn có thể thay đổi giá trị tại chỗ mà không cần tạo đối tượng mới. Tuy nhiên, nó phụ thuộc vào cách bạn thay đổi giá trị:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Nếu bạn gán một danh sách mới để a, sau đó id của nó sẽ thay đổi, vì vậy nó sẽ không ảnh hưởng bc's giá trị:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Số nguyên là bất biến, vì vậy bạn không thể thay đổi giá trị mà không tạo đối tượng mới:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1

1
idkhông nhất thiết là một vị trí bộ nhớ. Như các tài liệu nói, điều này trả về "danh tính, một số nguyên được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt vòng đời của nó." CPython tình cờ sử dụng địa chỉ bộ nhớ làm địa chỉ id, nhưng các triển khai Python khác có thể không. PyPy, ví dụ, không. Và nói rằng "hai vars trỏ đến cùng một vị trí bộ nhớ" là gây hiểu lầm cho bất cứ ai hiểu nó theo kiểu C. "Hai tên cho cùng một đối tượng" vừa chính xác hơn vừa ít gây hiểu lầm hơn.
abarnert

@abarnert cảm ơn đã làm rõ, tôi đã cập nhật câu trả lời.
jurgenreza

7

trong ví dụ đầu tiên của a = b = c = [1, 2, 3]bạn, bạn thực sự đang nói:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Nếu bạn muốn đặt 'a' bằng 1, 'b' bằng '2' và 'c' bằng 3, hãy thử điều này:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

Hi vọng điêu nay co ich!


4

Nói một cách đơn giản, trong trường hợp đầu tiên, bạn đang gán nhiều tên cho một list . Chỉ có một bản sao của danh sách được tạo trong bộ nhớ và tất cả các tên đều đề cập đến vị trí đó. Vì vậy, thay đổi danh sách bằng cách sử dụng bất kỳ tên nào sẽ thực sự sửa đổi danh sách trong bộ nhớ.

Trong trường hợp thứ hai, nhiều bản sao có cùng giá trị được tạo trong bộ nhớ. Vì vậy, mỗi bản sao là độc lập với nhau.


3

Những gì bạn cần là đây:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)

3

Mã làm những gì tôi cần có thể là:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Kết quả:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.