Tại sao Python cho phép các chỉ mục lát ngoài phạm vi cho các chuỗi?


76

Vì vậy, tôi vừa xem qua những gì có vẻ như đối với tôi là một tính năng lạ của Python và muốn được làm rõ về nó.

Thao tác mảng sau đây phần nào có ý nghĩa:

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]

Tôi tưởng tượng nó thực sự chỉ là thêm giá trị này vào cuối, đúng không?
Tuy nhiên, tại sao tôi có thể làm điều này?

p[20:22] = [5,6]
p = [1,2,3,4,5,6]

Và thậm chí nhiều hơn thế này:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]

Điều này có vẻ giống như logic sai. Có vẻ như điều này nên ném một lỗi!

Bất kỳ lời giải thích?
-Có phải Python chỉ là một điều kỳ lạ không?
-Có mục đích gì không?
-Hay là tôi đang nghĩ về điều này một cách sai lầm?


2
Trong các ngôn ngữ khác, tôi luôn kết thúc việc viết loại nội dung này ở khắp nơi: if i > sequence.length(): return sequence.slice(0, sequence.length()) else sequence.slice(0, n)Điều này giống hệt như chỉ sử dụng sequence[:n]trong Python, nó giúp bạn tiết kiệm một câu lệnh if và 2 lệnh gọi tới length.
Bakuriu

4
BTW. Bạn có thể xem các lát như là "bộ". Vì vậy, p[20:22]một dãy gồm tất cả các phần tử có chỉ số từ 20 đến 22. Tập hợp rỗng là một tập hợp lệ. Điều đó khác với cách nói p[20]khẳng định sự tồn tại của phần tử có chỉ số 20. Do đó, sự khác biệt trong việc kiểm tra phạm vi giữa tìm kiếm một phần tử và một lát cắt phản ánh hai ý nghĩa khác nhau.
Bakuriu

2
Tôi nghĩ đây là một câu hỏi rộng hơn về lý do tại sao việc thêm chuỗi trong các lát của chuỗi có độ dài khác nhau được cho phép trong Python và lợi ích của nó là gì. Câu hỏi khác không đề cập đến phần bài tập của câu hỏi này. Nó chỉ nói về việc cắt lát.
Akaisteph7

Câu trả lời:


79

Một phần câu hỏi liên quan đến các chỉ số nằm ngoài phạm vi

Logic lát cắt tự động cắt các chỉ số theo độ dài của chuỗi.

Việc cho phép các chỉ số lát cắt mở rộng các điểm cuối trước đây đã được thực hiện để thuận tiện. Sẽ rất khó khăn khi phải kiểm tra phạm vi mọi biểu thức và sau đó điều chỉnh các giới hạn theo cách thủ công, vì vậy Python sẽ làm điều đó cho bạn.

Hãy xem xét trường hợp sử dụng muốn hiển thị không quá 50 ký tự đầu tiên của tin nhắn văn bản.

Cách dễ dàng (những gì Python làm bây giờ):

preview = msg[:50]

Hoặc cách khó (tự kiểm tra giới hạn):

n = len(msg)
preview = msg[:50] if n > 50 else msg

Việc thực hiện thủ công logic đó để điều chỉnh các điểm cuối sẽ dễ bị quên, dễ bị sai (cập nhật 50 ở hai nơi), dài dòng và sẽ chậm. Python di chuyển logic đó vào bên trong của nó nơi nó thành công, tự động, nhanh chóng và chính xác. Đây là một trong những lý do tôi yêu thích Python :-)

Một phần câu hỏi liên quan đến độ dài bài tập không khớp với độ dài đầu vào

OP cũng muốn biết lý do để cho phép các phép gán, chẳng hạn như p[20:100] = [7,8]mục tiêu gán có độ dài khác (80) với độ dài dữ liệu thay thế (2).

Dễ dàng nhất để xem động lực bằng cách tương tự với các chuỗi. Hãy xem xét "five little monkeys".replace("little", "humongous"),. Lưu ý rằng mục tiêu "nhỏ" chỉ có sáu chữ cái và "humongous" có chín. Chúng ta có thể làm tương tự với các danh sách:

>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'

Tất cả điều này đều thuận tiện.

Trước khi ra đời các phương thức copy ()clear () , chúng thường là những thành ngữ phổ biến:

s[:] = []           # clear a list
t = u[:]            # copy a list

Ngay cả bây giờ, chúng tôi sử dụng điều này để cập nhật danh sách khi lọc:

s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values

Hy vọng những ví dụ thực tế này cung cấp một cái nhìn tốt về lý do tại sao cắt hoạt động như nó.


2
"Ngay cả bây giờ, chúng tôi sử dụng điều này để cập nhật danh sách khi lọc [ví dụ bằng cách sử dụng s[:]] " - Bạn có thể mở rộng lý do tại sao bạn sử dụng s[:] =ở đó, thay vì chỉ s =? Tôi chưa bao giờ thấy bất cứ ai sử dụng s[:] =trong ngữ cảnh của một dòng chẳng hạn như những gì bạn đã viết ở đó. Câu trả lời tốt nếu không!
Quuxplusone 10/02/19

10
@Quuxplusone: Phép gán Slice làm thay đổi danh sách đã được tham chiếu bởi s; sử dụng s = liên kết s lại để tham chiếu đến danh sách mới. Nếu danh sách có thể được tiếp cận thông qua nhiều tên và bạn muốn đột biến hiển thị với tất cả các tên, thì việc gán lát là điều bạn muốn. Ngoài ra, nếu slà toàn cục, việc gán lại ssẽ yêu cầu một globalkhai báo, nhưng việc gán lát cắt sẽ có tác dụng tương tự ngay cả khi không có globalcâu lệnh.
Daniel Pryden

24

Các tài liệu có câu trả lời của bạn:

s[i:j]: lát cắt stừ iđến j(ghi chú (4))

(4) Phần stừ iđến jđược định nghĩa là chuỗi các mục có chỉ mục knhư vậy i <= k < j. Nếu ihoặc jlớn hơn len(s), hãy sử dụnglen(s) . Nếu iđược bỏ qua hoặc None, sử dụng 0. Nếu j được bỏ qua hoặc None, sử dụng len(s). Nếu ilớn hơn hoặc bằng j, lát trống.

Các tài liệu vềIndexError xác nhân hành vi này:

ngoại lệ IndexError

Được tăng lên khi chỉ số con của chuỗi nằm ngoài phạm vi. (Các chỉ số lát cắt bị cắt bớt âm thầm để nằm trong phạm vi cho phép; nếu một chỉ số không phải là số nguyên, TypeErrorsẽ được nâng lên.)

Về cơ bản, những thứ như p[20:100]đang được giảm xuống p[len(p):len(p]. p[len(p):len(p]là một lát trống ở cuối danh sách và việc gán danh sách cho nó sẽ sửa đổi phần cuối của danh sách để chứa danh sách đã nói. Do đó, nó hoạt động giống như thêm / mở rộng danh sách ban đầu.

Hành vi này giống như những gì xảy ra khi bạn gán một danh sách cho một lát trống ở bất kỳ đâu trong danh sách ban đầu. Ví dụ:

In [1]: p = [1, 2, 3, 4]

In [2]: p[2:2] = [42, 42, 42]

In [3]: p
Out[3]: [1, 2, 42, 42, 42, 3, 4]

3
Tôi không nghĩ OP đang hỏi cách cắt hoạt động, anh ấy đang yêu cầu lý do đằng sau sự lựa chọn thiết kế.
Primusa

2
@Primusa - Tôi tin rằng họ đang hỏi cả hai . Điều này giải thích cách thực hiện , điều này rất tốt để biết vì nó giải thích tại sao hành vi đó không bị phá vỡ. Lý do tại sao có lẽ được chôn sâu trong một trong những danh sách gửi thư ở đâu đó.
gddc 10/02/19

Câu trả lời hay nhưng điều này không giải thích tại sao các số mới được thêm vào cuối danh sách.
Atirag

1
@Atirag Tôi đã thêm một lời giới thiệu nhỏ về nó cho hoàn chỉnh.
iz_

1
@Atirag Lập chỉ mục rất khác với cắt lát; lập chỉ mục luôn đề cập đến các giá trị.
iz_
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.