2017-12-29 136 views
12

Tôi đã xem qua đoạn mã sau (loại):Danh sách này được mở rộng như thế nào với nhiệm vụ cắt?

my_list = [1, [2, 3, 4], 5] 
my_list[1:2] = my_list[1] 

Sau khi chạy hai dòng này, biến my_list sẽ [1, 2, 3, 4, 5]. Khá hữu ích cho việc mở rộng danh sách lồng nhau.

Nhưng tại sao nó thực sự làm những gì nó làm?

tôi sẽ giả định rằng tuyên bố my_list[1:2] = my_list[1] sẽ làm một trong các cách sau:

  • chỉ cần đặt [2, 3, 4] vào vị trí thứ hai trong danh sách (trong đó nó đã có)
  • đưa ra một số loại "quá nhiều giá trị để giải nén "lỗi", từ việc cố gắng đặt ba giá trị (cụ thể là 2,3,4) vào một thùng chứa chỉ có chiều dài 1 (cụ thể là my_list[1:2]). (Lặp lại ở trên với một mảng NumPy thay vì một kết quả danh sách trong một lỗi tương tự.)

câu hỏi khác (ví dụ How assignment works with python list slice) có xu hướng không quan tâm nhiều đến sự khác biệt giữa kích thước của slice được thay thế, và kích thước của các mục bạn đang thay thế bằng. (Hãy để một mình giải thích lý do tại sao nó hoạt động theo cách đó.)

+0

'my_list = [1,2,3]; my_list [1: 2] = [9,8,7] '=>' [1, 9, 8, 7,3] 'Tôi nghĩ đó là hành vi bình thường của python trong khi thay thế slice bằng danh sách – splash58

+0

Sử dụng' my_list này [1: 2] = [my_list [1]] ' – Artier

+0

Tôi đã chỉnh sửa một trong các câu trả lời ở đó để đề cập rõ ràng rằng bạn có thể sử dụng các danh sách có kích thước khác nhau ở cả hai phía của bài tập. * "Hãy để một mình giải thích lý do tại sao nó hoạt động theo cách của nó." * Bạn mong đợi loại câu trả lời nào? https://stackoverflow.com/a/10623352/2301450 và https://stackoverflow.com/a/35632876/2301450 đều chính xác. – vaultah

Trả lời

5

Slice assignment thay thế một phần được chỉ định của danh sách có thể lặp lại ở phía bên tay phải. dài hơn lát. Lấy câu hỏi theo giá trị khuôn mặt, lý do tại sao điều này là như vậy là bởi vì nó thuận tiện.

Bạn không thực sự gán cho lát, tức là Python không tạo ra một đối tượng lát có chứa các giá trị được chỉ định từ danh sách và sau đó thay đổi các giá trị này. Một lý do không hiệu quả là việc slicing trả về một danh sách mới, vì vậy thao tác này sẽ không thay đổi danh sách gốc.

Ngoài ra, hãy xem this question, nhấn mạnh rằng việc phân công cắt và cắt hoàn toàn khác nhau.

+0

Câu trả lời này tương tự như một số câu trả lời khác, ngoại trừ một câu, mà tôi nghĩ là rất quan trọng: "lý do tại sao điều này là vì nó thuận tiện". Danh sách phân công slice đã được thực hiện cụ thể để có hành vi này, không nhất thiết vì nó hợp lý (tôi nghĩ rằng nó không phải là hợp lý ở tất cả) nhưng vì nó thuận tiện. Cảm ơn! – acdr

-1
my_list[1:2] = my_list[1], Its simply a inserting statement, it translate to 

Chúng tôi chỉ đang thử một số yếu tố giữa my_list1from my_list [1] với my_list2. Bây giờ nó rất giống với list.extend (list1), chỉ cần chúng ta chèn vào giữa một danh sách thay vì ở cuối

+3

Điều này chỉ đơn giản là tái phát biểu những gì phát biểu, chứ không phải cách nó thực sự hoạt động dưới mui xe. – acdr

2

ngắn Trả lời:

my_list[1:2] = my_list[1] sẽ thay thế các nội dung từ chỉ số 1 đến số 2 của my_list với nội dung của hiện tại chỉ số 1 của my_list

Giải thích:

Hãy xem hai cuộc phẫu thuật cắt, rất giống nhau nhưng hoàn toàn khác biệt

  1. Điều này tạo ra các bản sao của danh sách và lưu trữ nó biến

    some_variable = some_list[2:5] 
    
  2. này thay thế nội dung của danh sách inplace, cho phép thay đổi độ dài của danh sách.

    some_list[2:5] = [1, 2, 3, 4] 
    

Khi bạn sử dụng phân tử =, nó gọi một hàm __setitem__. Trọng tâm của chúng tôi ở đây là trường hợp 2 ở trên. Theo tài liệu Python's Assignment Statement:

Nếu mục tiêu là một slicing: Khái niệm cơ bản trong tài liệu tham khảo là đánh giá. Nó sẽ tạo ra một đối tượng chuỗi có thể thay đổi (chẳng hạn như danh sách). Đối tượng được gán phải là đối tượng chuỗi cùng loại. Tiếp theo, các biểu thức giới hạn dưới và trên được đánh giá, trong đó có chúng hiện diện; giá trị mặc định bằng 0 và độ dài của chuỗi. Các giới hạn nên đánh giá các số nguyên. Nếu một trong hai giới hạn là tiêu cực, thì chiều dài của chuỗi sẽ được thêm vào. Các giới hạn kết quả được cắt bớt thành nằm giữa 0 và độ dài của chuỗi, bao gồm. Cuối cùng, đối tượng chuỗi được yêu cầu thay thế lát bằng các mục của dãy được gán . Chiều dài của lát có thể khác với chiều dài của chuỗi được chỉ định, do đó thay đổi độ dài của chuỗi mục tiêu , nếu chuỗi mục tiêu cho phép nó.

Trong trường hợp của chúng tôi my_list[1:2] = my_list[1], trăn cũng sẽ gọi __setitem__ như:

my_list.__setitem__(slice(1,2,None), [2, 3, 4]) 

Tham khảo slice tài liệu để biết những gì nó làm.

Vì vậy, khi bạn đã làm my_list[1:2] = my_list[1], bạn thay thế các nội dung từ chỉ số 1 đến số 2 của my_list với nội dung của hiện tại chỉ số 1 của my_list ví dụ [2, 3, 4].


Tôi nghĩ rằng bây giờ chúng tôi có thể trả lời tại sao giả định của bạn là không chính xác:

  • đặt [2, 3, 4] vào vị trí thứ hai trong danh sách (trong đó nó đã có)

No. Bởi vì __setitem__ không được gọi trên chỉ mục nhưng trên slice của chỉ mục bạn đã vượt qua.

  • đưa ra một số loại "quá nhiều giá trị để giải nén" lỗi, từ cố gắng để đưa ba giá trị (cụ thể là 2,3,4) vào một container chỉ dài 1 (cụ thể là my_list [1: 2]).

Một lần nữa không. Vì phạm vi chỉ mục của bạn tạo vùng chứa được thay thế bằng tập hợp giá trị mới.

1

Những gì bạn đang làm là slice assignment.

Chuyển nhượng để lát cũng có thể, và điều này thậm chí có thể thay đổi kích thước của danh sách hoặc xóa nó hoàn toàn

my_list[1:2] = my_list[1]

này thay thế lát my_list với các nội dung của my_list[1].

Bằng cách chỉ định my_list[1:2] ở phía bên trái của toán tử gán =, bạn đang nói với Python bạn muốn sử dụng gán phân đoạn.

my_list[1:2] = my_list[1] tương đương với my_list.__setitem__(slice(1, 2, None), my_list[1])

Trong slice (1, 2, Không), 1 là bắt đầu, 2 là dừng lại, và None là bước và không bắt buộc.

2

Dưới đây là bit có liên quan từ Python Language Reference

Nếu mục tiêu là một cắt: Khái niệm cơ bản trong tài liệu tham khảo là đánh giá. Nó sẽ tạo ra một đối tượng chuỗi có thể thay đổi (chẳng hạn như danh sách). Đối tượng được gán phải là đối tượng chuỗi cùng loại. Tiếp theo, các biểu thức giới hạn dưới và trên được đánh giá, trong đó có chúng hiện diện; giá trị mặc định bằng 0 và độ dài của chuỗi. Các giới hạn nên đánh giá các số nguyên. Nếu một trong hai giới hạn là tiêu cực, thì chiều dài của chuỗi sẽ được thêm vào. Các giới hạn kết quả được cắt bớt thành nằm giữa 0 và độ dài của chuỗi, bao gồm. Cuối cùng, đối tượng chuỗi được yêu cầu thay thế lát bằng các mục của chuỗi được gán . Chiều dài của lát có thể khác với chiều dài của chuỗi được chỉ định, do đó thay đổi độ dài của chuỗi mục tiêu , nếu chuỗi đích cho phép nó.

Hành vi này làm cho tinh thần chất lượng bởi vì khi bạn cắt một danh sách bạn nhận được một danh sách phụ để thay thế mà với một danh sách khác không nên thêm một mức độ làm tổ. Cho phép nó thay đổi độ dài của danh sách là một lựa chọn thiết kế. Có thể có các lựa chọn khác như ví dụ numpy của bạn.

0

Điều bạn đang làm là chèn phần tử thông qua cắt. Tôi sẽ giải thích mọi thứ theo từng phần. Hơn một chèn nó có thể được hiểu là thêm một mục vào danh sách của bạn sau khi cắt danh sách đích trong một phạm vi mong muốn. Bây giờ để giải thích từng dòng chi tiết:

my_list[1:2] 

Phần đó giống như nói với Python; "Tôi muốn lấy các giá trị (slice) từ chỉ mục 1 đến chỉ mục trước 2 (không bao gồm 2, tôi sẽ giải thích bằng một ví dụ khác về sau)". Sau đó bạn gán một giá trị cho những giá trị:

my_list[1:2] = my_list[1] #The same as my_list[1:2] = [2,3,4] 

Bây giờ bạn biết những gì phần đầu tiên thực hiện, bên cạnh đó là có thêm mục ở phía bên phải của '=' nhà điều hành, do đó bạn có thể giải thích nó như thế này; "Tôi muốn cắt từ chỉ mục 1 sang mọi thứ trước 2 (một lần nữa, loại trừ chỉ mục 2) và sau đó thêm danh sách của tôi [2,3,4]". Bây giờ ở đây có một ví dụ khác để bạn hiểu tốt hơn tôi hy vọng.

problemList = [1, [2, 3, 4], 5] 
problemList[1:2] = problemList[1] #The same as problemList[1:2] = [2,3,4] 
analogProblemL = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5] 

insertList = [12,13,14] 
myList = [1, [2, 3, 4], 5,6,7,8,9] 
myList[3:6] = insertList 
analogFstList = [1,[2,3,4] ,5] + insertList + [9] #Output : [1,[2,3,4],5,12,13,14,9] 

myScnList = [1, [2, 3, 4], 5] 
myScnList[1:3] = [2,3,4] 
analogScnList = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5] 

Các dòng tiếp theo sẽ như thế nào nếu đó là một khung hình ảnh động vì vậy nó dễ dàng hơn để giải thích:

[1,2,3,4,5] #List before line of code: myList[1:3] = [12,13,14] 
[1,|2,3|,4,5] #The moment where you slice the list with: myList[1:3]. Please notice that the characters used '|' are for representing the slice. 
[1] + [12,13,14] + [4,5] #After assigning what the slice should be changed for. It's like excracting from the whole list the values [2,3] and changing it for [12,13,14]. 
[1,12,13,14,4,5] #Final list after running the code. 

Một số tài liệu tham khảo dùng cho câu trả lời này: http://effbot.org/zone/python-list.htm Understanding Python's slice notation How assignment works with python list slice

Hope nó rất hữu ích cho bạn.

1

Những gì bạn đang cố gắng ở đây được gọi là Slice Assingnment. Trong python, bạn có thể chỉ định một trường hợp có thể lặp lại (my_list[1] trong trường hợp của bạn) thành một lát khác có thể lặp lại (my_list[0:1] trong trường hợp của bạn). Cho phép đi bộ qua một số ví dụ để hiểu những gì nó thực sự có nghĩa là:

>>> l = [1,2,3,4,5] 
>>> b = [6,7,8] 
>>> l[0:3] = b 
>>> l 
>>> [6, 7, 8, 4, 5] 

Vì vậy, những gì xảy ra ở đây là một phần của danh sách l cho 0,1,2 phêìn trong đó bao gồm các yếu tố 1,2,3 được thay thế bởi các yếu tố của danh sách b6,7,8. Tuy nhiên trong trường hợp này, kích thước của slice và các phần tử được thay thế sẽ xảy ra bằng cơ hội.

Vậy điều gì sẽ xảy ra khi kích thước lát và iterable phải được thay thế không bằng nhau

>>> l = [1,2,3,4,5] 
>>> b = [6,7,8] 
>>> l[0:4] = b 
>>> l 
>>> [6,7,8,5] 

ý rằng hoạt động này đã không tạo ra bất kỳ lỗi, thay vào đó, nó chỉ sao chép bất cứ yếu tố có sẵn với toàn bộ phần thái lát . Trong trường hợp này, các phần tử đã cắt lát là 1,2,3,4 được thay thế bằng 6,7,8

Trong ví dụ trước có thể thay thế được thay thế nhỏ hơn.Chuyện gì xảy ra nếu phần lát nhỏ

>>> l = [1,2,3,4,5] 
>>> b = [6,7,8] 
>>> l[0:1] = b 
>>> l 
>>> [6,7,8,2,3,4,5] 

Vì vậy, bây giờ chúng ta có thể thấy rằng chỉ phần tử đầu tiên được thay thế bằng toàn bộ iterable b.

Bạn cũng có thể sử dụng hành vi này để xóa một phần cụ thể của danh sách (Tôi thấy thuận tiện trong một số trường hợp).

>>> l = [1,2,3,4,5] 
>>> l[0:2] = [] 
>>> l 
>>> [3,4,5] 

Hai yếu tố đầu tiên được xóa rất tiện lợi tại đây.

Ví dụ trong câu hỏi của bạn tương tự như ví dụ tôi đã đăng ở trên, ngoại trừ trường hợp của bạn, có thêm một bước là giải nén giá trị danh sách. Việc giải nén giá trị danh sách xảy ra mỗi khi bạn gán danh sách vào danh sách khác. Một ví dụ ngắn

>>> l = [[1]] 
>>> a = [] 
>>> a = l[0] 
>>> a 
>>> [1] 

dụ của bạn bây giờ:

#Replace the slice [0:1] with my_list[1] perform unpacking list values as well 
>>> my_list[1:2] = my_list[1] 
>>> [1,2,3,4,5] 

Cũng lưu ý rằng việc chuyển nhượng lát chỉ có thể nếu bạn gán iterable để một lát. Nếu bạn cố gắng gán một int hoặc cái gì đó không phải là một iterable để một trăn slice sẽ ném một lỗi.

>>> l = [1,2,3,4,5] 
>>> b = [6,7,8] 
>>> l[0:1] = b[1] 
>>> Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: can only assign an iterable 

Đó là lý do tại sao trong trường hợp của bạn my_list[1] không gây ra lỗi vì nó có thể lặp lại được.

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