2010-10-29 44 views
5

Đây là một câu hỏi đơn giản về cách Python xử lý dữ liệu và các biến. Tôi đã thực hiện rất nhiều thử nghiệm và đã Python chủ yếu là tìm ra, ngoại trừ điều này sẽ giúp vấp ngã tôi lên:Gán giá trị cho một phần tử của một lát trong Python

[sửa: Tôi tách ra và sắp xếp lại các ví dụ cho rõ ràng]

Ví dụ 1:

>>> a = [[1], 2] 
>>> a[0:1] 
[[1]] 
>>> a[0:1] = [[5]] 
>>> a 
[[5], 2] # The assignment worked. 

Ví dụ 2:

>>> a = [[1], 2] 
>>> a[0:1][0] 
[1] 
>>> a[0:1][0] = [5] 
>>> a 
[[1], 2] # No change? 

Ví dụ 3:

>>> a = [[1], 2] 
>>> a[0:1][0][0] 
1 
>>> a[0:1][0][0] = 5 
>>> a 
[[5], 2] # Why now? 

Ai cũng có thể giải thích cho tôi điều gì đang xảy ra ở đây?

Cho đến nay các câu trả lời dường như tuyên bố rằng a[0:1] trả về danh sách mới chứa tham chiếu đến phần tử đầu tiên là a. Nhưng tôi không thấy cách giải thích Ví dụ 1.

+1

Câu hỏi thú vị. Hãy xem Alex nói gì. :) –

Trả lời

7

a [0: 1] đang trả về mảng mới chứa tham chiếu đến mảng [1], do đó bạn sẽ sửa đổi mảng bên trong thông qua cuộc gọi tham chiếu .

Lý do trường hợp đầu tiên không sửa đổi mảng [1] là bạn chỉ định mảng bên ngoài được sao chép một giá trị mảng bên trong mới.

Dòng dưới cùng - [0: 1] trả về bản sao dữ liệu, nhưng dữ liệu bên trong không được sao chép.

+0

@ dln385: Câu cuối cùng của bài đăng này trả lời câu hỏi của bạn. Dữ liệu bên trong (trong trường hợp của bạn, số) không được sao chép, do đó sửa đổi nó sẽ thay đổi bản gốc. –

+0

Câu cuối cùng giải thích tại sao 'a [0: 1] [0] [0] = 5' hoạt động. Nhưng 'a [0: 1] = [[5]]' không sửa đổi dữ liệu bên trong, và vì vậy _shouldn't_ làm việc. Tôi nghĩ rằng nó phải được xử lý như một trường hợp đặc biệt, như [pyfunc] (http://stackoverflow.com/questions/4055515/assigning-a-value-to-an-element-of-a-slice-in-python/ 4055556 # 4055556) dường như ngụ ý. – dln385

+4

Có, khi statetement gán (tức là, "=" tín hiệu) đến sau một tham chiếu slice nó là một điều hoàn toàn khác nhau hơn khi các hoạt động khác được thực hiện. Để rõ ràng: '>>> a [0: 1] = [[5]]' tương đương với 'a .__ setitem __ (slice (0,1, None), [5])' --- trong khi 'a [ 0: 1] [0] = 5' giống như: 'a .__ getitem __ (slice (0,1, None)) .__ setitem __ (0, 5)' – jsbueno

3

Sự hiểu biết của tôi là việc cắt trả về một đối tượng mới. Đó là giá trị trả về của nó là một danh sách mới.

Do đó bạn không thể sử dụng một toán tử gán để thay đổi các giá trị của danh sách ban đầu

>>> a = [[1], 2, 3] 
>>> k = a[0:2] 
>>> id(a) 
4299352904 
>>> id(k) 
4299353552 
>>> 

>>> id(a) 
4299352904 
>>> id(a[0:2]) 
4299352832 

số lượt hơn dọc theo dòng

>>> k = 5 
>>> 
>>> id(k) 
4298182344 
>>> a[0] = [1,2] 
>>> a 
[[1, 2], 2, 3] 
>>> id(a) 
4299352904 
>>> 

[Chỉnh sửa: trên phần thứ hai của câu hỏi]

>>> a[0:1] = [[5]] 

Ký hiệu sau cũng được gọi là phổ biến là Phân bổ lát Hành vi của danh sách dựng sẵn là nguyên tử (xóa + chèn) xảy ra trong một lần. Sự hiểu biết của tôi là điều này không được phép cho chuỗi tùy chỉnh.

+0

Điều này là chính xác. Cắt lát trả về một đối tượng mới. Điều này cũng đúng với việc gán các khóa từ điển cho một biến mới (ví dụ: 'foo = mydict [bar]'). Các thay đổi đối với slice được chỉ định không sửa đổi tham chiếu ban đầu vì chúng là một bản sao. – jathanism

1

Có ba hoạt động riêng biệt với chỉ số, tất cả đều được dịch sang phương pháp gọi:

  • a[i] = b =>a.__setitem__(i, b)
  • del a[i] =>a.__delitem__(i)
  • a[i] sử dụng như một biểu thức =>a.__getitem__(i)

Đây a, bi là các biểu thức và i có thể chứa slice objects được tạo bằng cú pháp viết tắt của dấu hai chấm. Ví dụ:

>>> class C(object): 
...  def __setitem__(self, *a): 
...    print a 
... 
>>> C()[1] = 0 
(1, 0) 
>>> C()['foo'] = 0 
('foo', 0) 
>>> C()['foo':'bar'] = 0 
(slice('foo', 'bar', None), 0) 
>>> C()['foo':'bar',5] = 0 
((slice('foo', 'bar', None), 5), 0) 

Vì vậy, những gì đang xảy ra trong ví dụ thứ ba của bạn là thế này:

a[0:1][0][0] = 5 

trở thành

a.__getitem__(slice(0,1)).__getitem__(0).__setitem__(0, 5) 

Đầu tiên __getitem__ trả về một bản sao của một phần của danh sách, nhưng thứ hai __getitem__ trả về danh sách thực tế bên trong danh sách đó, sau đó được sửa đổi bằng cách sử dụng __setitem__.

ví dụ thứ hai của bạn, mặt khác trở nên

a.__getitem__(slice(0,1)).__setitem__(0, 5) 

Vì vậy __setitem__ đang được kêu gọi bản sao thái lát, để lại danh sách ban đầu nguyên vẹn.

Các vấn đề liên quan