Câu hỏi Hiểu chú thích slice của Python


Tôi cần một lời giải thích tốt (tài liệu tham khảo là một cộng) trên ký hiệu slice của Python.

Đối với tôi, ký hiệu này cần một chút nhặt lên.

Nó trông cực kỳ mạnh mẽ, nhưng tôi đã không hoàn toàn có đầu xung quanh nó.


2300
2018-02-03 22:31


gốc




Các câu trả lời:


Nó khá đơn giản thực sự:

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

Ngoài ra còn có step giá trị, có thể được sử dụng với bất kỳ điều nào ở trên:

a[start:end:step] # start through not past end, by step

Điểm mấu chốt cần nhớ là :end giá trị đại diện cho giá trị đầu tiên không phải trong lát đã chọn. Vì vậy, sự khác biệt beween end và start là số phần tử được chọn (nếu step là 1, mặc định).

Các tính năng khác là start hoặc là end có thể là một tiêu cực số, có nghĩa là nó đếm từ cuối mảng thay vì bắt đầu. Vì thế:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

Tương tự, step có thể là số âm:

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

Python là loại lập trình viên nếu có ít hơn các mục bạn yêu cầu. Ví dụ: nếu bạn yêu cầu a[:-2] và a chỉ chứa một phần tử, bạn sẽ nhận được một danh sách trống thay vì một lỗi. Đôi khi bạn thích lỗi, vì vậy bạn phải biết rằng điều này có thể xảy ra.


3113
2018-02-03 22:48



Các kiểu nội trang lát cắt trả về một bản sao nhưng không phổ biến. Đáng chú ý là cắt mảng NumPy trả về một lượt xem chia sẻ bộ nhớ với bản gốc. - Beni Cherniavsky-Paskin
Điều gì sẽ làm điều này? [::-2]  Tôi đã thử nó nhưng tôi không hiểu nó. Ví dụ với danh sách này a = ['1', '2', '3', '4', '5'] . - RodriKing


Các Hướng dẫn Python nói về nó (di chuyển xuống một chút cho đến khi bạn nhận được một phần về việc cắt).

Biểu đồ nghệ thuật ASCII cũng hữu ích để ghi nhớ cách các slice hoạt động:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

Một cách để ghi nhớ cách thức hoạt động của slice là suy nghĩ của các chỉ mục như chỉ giữa ký tự, với cạnh trái của ký tự đầu tiên được đánh số 0. Sau đó, cạnh phải của ký tự cuối cùng của một chuỗi ký tự n ký tự có chỉ mục n.


396
2018-02-03 22:49



Đối với các lát có các bước tiêu cực, tôi thấy nghệ thuật ASCII này khó hiểu và nó sẽ mở rộng đến -6 kể từ đó 'ApleH'[:-6:-1] là một lát hợp lệ và khác với việc sử dụng -5 - Chris_Rands
"Một cách để ghi nhớ cách thức hoạt động của các slice là suy nghĩ về các chỉ số như chỉ giữa các ký tự" - đây là một cách tuyệt vời để suy nghĩ về nó - jusopi


Liệt kê các khả năng được phép theo ngữ pháp:

>>> seq[:]                # [seq[0],   seq[1],          ..., seq[-1]    ]
>>> seq[low:]             # [seq[low], seq[low+1],      ..., seq[-1]    ]
>>> seq[:high]            # [seq[0],   seq[1],          ..., seq[high-1]]
>>> seq[low:high]         # [seq[low], seq[low+1],      ..., seq[high-1]]
>>> seq[::stride]         # [seq[0],   seq[stride],     ..., seq[-1]    ]
>>> seq[low::stride]      # [seq[low], seq[low+stride], ..., seq[-1]    ]
>>> seq[:high:stride]     # [seq[0],   seq[stride],     ..., seq[high-1]]
>>> seq[low:high:stride]  # [seq[low], seq[low+stride], ..., seq[high-1]]

Tất nhiên nếu (high-low)%stride != 0thì điểm cuối sẽ thấp hơn một chút high-1.

Nếu stride là tiêu cực, thứ tự được thay đổi một chút kể từ khi chúng tôi đếm ngược:

>>> seq[::-stride]        # [seq[-1],   seq[-1-stride],   ..., seq[0]    ]
>>> seq[high::-stride]    # [seq[high], seq[high-stride], ..., seq[0]    ]
>>> seq[:low:-stride]     # [seq[-1],   seq[-1-stride],   ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]

Mở rộng cắt (với dấu phẩy và hình elip) chủ yếu được sử dụng bởi các cấu trúc dữ liệu đặc biệt (như Numpy); các trình tự cơ bản không hỗ trợ chúng.

>>> class slicee:
...     def __getitem__(self, item):
...         return `item`
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

310
2018-02-03 23:08



có điều gì lạ xảy ra nếu stride là âm như trong [::-1]? - Charlie Parker
@CharlieParker Khi strike là tiêu cực, nó đếm ngược từ high đến low. - ephemient
Trên thực tế vẫn còn một số thứ còn lại, ví dụ: nếu tôi gõ 'apple' [4: -4: -1] tôi nhận được 'elp', python đang dịch -4 thành 1 có lẽ? - liyuan


Các câu trả lời ở trên không thảo luận về phân bổ slice:

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

Điều này cũng có thể làm rõ sự khác biệt giữa việc cắt và lập chỉ mục.


199
2018-01-18 21:37



bạn có thể giải thích điều thứ hai không? - chandresh


Giải thích ký hiệu slice của Python

Tóm lại, các dấu hai chấm (:) trong ký hiệu chỉ số (subscriptable[subscriptarg]) làm cho ký hiệu slice - trong đó có các đối số tùy chọn, start, stop, step:

sliceable[start:stop:step]

Cắt bằng Python là một cách nhanh chóng về mặt tính toán để truy cập các phần dữ liệu của bạn một cách có phương pháp. Theo tôi, thậm chí là một lập trình viên Python trung gian, nó là một khía cạnh của ngôn ngữ mà nó cần thiết để làm quen với nó.

Định nghĩa quan trọng

Để bắt đầu, hãy xác định một vài thuật ngữ:

khởi đầu: chỉ mục bắt đầu của slice, nó sẽ bao gồm phần tử tại chỉ mục này trừ khi nó giống như dừng lại, mặc định là 0, tức là chỉ mục đầu tiên. Nếu đó là tiêu cực, nó có nghĩa là để bắt đầu n các mục từ cuối.

dừng lại: chỉ mục kết thúc của slice, nó không phải bao gồm phần tử tại chỉ mục này, mặc định là độ dài của chuỗi được cắt, có nghĩa là, lên đến và bao gồm phần cuối.

bậc thang: số tiền mà theo đó chỉ số tăng lên, mặc định là 1. Nếu đó là số âm, bạn sẽ cắt qua lần lặp lại theo thứ tự ngược lại.

Cách lập chỉ mục hoạt động

Bạn có thể thực hiện bất kỳ số dương hoặc số âm nào. Ý nghĩa của các số dương là đơn giản, nhưng đối với các số âm, giống như các chỉ mục trong Python, bạn đếm ngược từ cuối cho khởi đầu và dừng lạivà cho bậc thang, bạn chỉ cần giảm chỉ mục của mình. Ví dụ này là từ hướng dẫn của tài liệu, nhưng tôi đã sửa đổi nó một chút để cho biết mục nào trong chuỗi mà mỗi chỉ mục tham chiếu:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

Cách hoạt động của Slicing

Để sử dụng ký hiệu slice với một chuỗi hỗ trợ nó, bạn phải bao gồm ít nhất một dấu hai chấm trong dấu ngoặc vuông theo sau chuỗi (thực tế là thực hiện __getitem__ phương pháp của chuỗi, theo mô hình dữ liệu Python.)

Ký hiệu slice hoạt động như sau:

sequence[start:stop:step]

Và nhớ lại rằng có mặc định cho khởi đầu, dừng lạibậc thang, do đó, để truy cập các giá trị mặc định, chỉ cần bỏ qua đối số.

Ký hiệu slice để có được chín phần tử cuối cùng từ một danh sách (hoặc bất kỳ chuỗi nào khác hỗ trợ nó, giống như một chuỗi) sẽ trông như thế này:

my_list[-9:]

Khi tôi thấy điều này, tôi đọc phần trong ngoặc đơn là "9 từ cuối, đến cuối". (Thực ra, tôi viết tắt nó là "-9, on")

Giải trình:

Ký hiệu đầy đủ là

my_list[-9:None:None]

và để thay thế các giá trị mặc định (thực sự khi step là tiêu cực, stopmặc định là -len(my_list) - 1, vì thế None cho dừng thực sự chỉ có nghĩa là nó đi đến bất cứ bước cuối cùng sẽ đưa nó đến):

my_list[-9:len(my_list):1]

Các đại tràng, :, là những gì nói với Python bạn đang cho nó một lát và không phải là một chỉ mục thường xuyên. Đó là lý do tại sao cách thành ngữ tạo ra một bản sao nông của các danh sách trong Python 2 là

list_copy = sequence[:]

Và thanh toán bù trừ chúng là:

del my_list[:]

(Python 3 nhận được một list.copy và list.clear phương pháp.)

Khi nào step là số âm, giá trị mặc định cho start và stop thay đổi

Theo mặc định, khi step đối số trống (hoặc None), nó được gán cho +1.

Nhưng bạn có thể vượt qua trong một số nguyên âm, và danh sách (hoặc hầu hết các lát cắt tiêu chuẩn khác) sẽ được cắt từ đầu đến đầu.

Do đó, một lát âm sẽ thay đổi các giá trị mặc định cho start và stop!

Xác nhận điều này trong nguồn

Tôi muốn khuyến khích người dùng đọc nguồn cũng như tài liệu. Các mã nguồn cho các đối tượng slice và logic này được tìm thấy ở đây. Đầu tiên chúng tôi xác định xem step là tiêu cực:

 step_is_negative = step_sign < 0;

Nếu vậy, giới hạn dưới là -1  có nghĩa là chúng ta cắt tất cả các con đường lên đến và bao gồm cả bắt đầu, và giới hạn trên là chiều dài trừ đi 1, có nghĩa là chúng ta bắt đầu ở cuối. (Lưu ý rằng ngữ nghĩa của điều này -1 Là khác nhau từ một -1 rằng người dùng có thể chuyển các chỉ mục bằng Python cho biết mục cuối cùng.)

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

Nếu không thì step là dương, và giới hạn dưới sẽ bằng không và giới hạn trên (mà chúng ta đi tới nhưng không bao gồm) chiều dài của danh sách cắt lát.

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

Sau đó, chúng tôi có thể cần áp dụng các giá trị mặc định cho start và stop - mặc định sau đó cho start được tính như giới hạn trên khi step là tiêu cực:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

stop, giới hạn dưới:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

Đặt tên cho một lát của bạn!

Bạn có thể thấy hữu ích khi tách hình thành slice để chuyển nó sang list.__getitem__ phương pháp (đó là những gì các dấu ngoặc vuông làm). Ngay cả khi bạn không mới với nó, nó giữ mã của bạn dễ đọc hơn để những người khác có thể phải đọc mã của bạn có thể dễ dàng hiểu những gì bạn đang làm.

Tuy nhiên, bạn không thể chỉ định một số số nguyên được phân cách bằng dấu hai chấm cho một biến. Bạn cần sử dụng đối tượng slice:

last_nine_slice = slice(-9, None)

Đối số thứ hai, None, là bắt buộc, để đối số đầu tiên được hiểu là start tranh luận bằng không nó sẽ là stop tranh luận.

Sau đó, bạn có thể truyền đối tượng slice cho chuỗi của mình:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

Điều thú vị là phạm vi cũng mất lát:

>>> range(100)[last_nine_slice]
range(91, 100)

Cân nhắc bộ nhớ:

Vì các lát của các danh sách Python tạo các đối tượng mới trong bộ nhớ, một chức năng quan trọng khác cần lưu ý là itertools.islice. Thông thường bạn sẽ muốn lặp qua một slice, không chỉ có nó được tạo tĩnh trong bộ nhớ. islice là hoàn hảo cho việc này. Một báo trước, nó không hỗ trợ các đối số phủ định start, stop, hoặc là step, vì vậy nếu đó là một vấn đề bạn có thể cần phải tính toán các chỉ số hoặc đảo ngược các iterable trước.

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

và bây giờ:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

Thực tế là các lát danh sách tạo bản sao là một tính năng của các danh sách. Nếu bạn đang cắt các đối tượng nâng cao như một khung dữ liệu Pandas, nó có thể trả về một khung nhìn trên bản gốc và không phải là một bản sao.


186
2017-07-12 13:19



Xin chào @aaronhall, bài đăng tuyệt vời! Bạn có cân nhắc việc đóng góp bài đăng này vào cuốn sách Python trung gian không - github.com/yasoob/intermediatePython/issues/153 ? - neowulf33
Chỉ muốn nói cảm ơn bạn rất nhiều cho bài đăng này, tôi đã viết Python trong một thời bây giờ và vẫn trở lại điều này khi tôi tạo ra bởi một lỗi tất cả các thời gian với slicing! - akkatracker
câu trả lời tốt nhất ở đây - kskyriacou


Và một vài điều không rõ ràng với tôi khi lần đầu tiên tôi nhìn thấy cú pháp cắt:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

Cách dễ dàng để đảo ngược trình tự!

Và nếu bạn muốn, vì một lý do nào đó, mọi mục thứ hai trong chuỗi đảo ngược:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]

124
2018-02-03 23:15





Tìm thấy bảng tuyệt vời này tại http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

84
2017-09-06 06:50





Trong Python 2,7

Cắt lát bằng Python

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

Hiểu chỉ định chỉ mục là rất quan trọng.

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

Khi bạn nói [a: b: c], bạn đang nói tùy thuộc vào dấu của c (tiến hoặc lùi), bắt đầu tại một và kết thúc tại b (không bao gồm phần tử tại chỉ mục bth). Sử dụng quy tắc lập chỉ mục ở trên và nhớ rằng bạn sẽ chỉ tìm thấy các phần tử trong phạm vi này:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

Nhưng phạm vi này tiếp tục theo cả hai hướng vô hạn:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

Ví dụ:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

Nếu bạn chọn a, b và c cho phép chồng chéo với phạm vi ở trên khi bạn đi ngang qua các quy tắc cho a, b, c ở trên, bạn sẽ có danh sách với các phần tử (được chạm trong khi truyền tải) hoặc bạn sẽ nhận được danh sách trống.

Một điều cuối cùng: nếu a và b bằng nhau, thì bạn cũng nhận được một danh sách trống:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]

84
2017-10-22 05:33



một ví dụ thú vị khác: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]mà kết quả [9] - Deviacium


Sau khi sử dụng nó một chút tôi nhận ra rằng các mô tả đơn giản nhất là nó là chính xác giống như các đối số trong vòng lặp for ...

(from:to:step)

bất kỳ trong số đó là tùy chọn

(:to:step)
(from::step)
(from:to)

thì việc lập chỉ mục tiêu cực chỉ cần bạn thêm chiều dài của chuỗi vào các chỉ số âm để hiểu nó.

Điều này làm việc cho tôi anyway ...


47
2018-02-19 20:52





Tôi thấy nó dễ dàng hơn để nhớ cách nó hoạt động, sau đó tôi có thể tìm ra bất kỳ sự kết hợp bắt đầu / dừng / bước cụ thể nào.

Đó là hướng dẫn để hiểu range() Đầu tiên:

def range(start=0, stop, step=1):  # illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

Bắt đầu từ start, tăng thêm step, đừng chạm vào stop. Rất đơn giản.

Điều cần nhớ về bước tiêu cực là stop luôn luôn là kết thúc bị loại trừ, cho dù nó cao hơn hay thấp hơn. Nếu bạn muốn cùng một lát theo thứ tự ngược lại, nó sạch sẽ hơn nhiều để thực hiện đảo ngược riêng biệt: ví dụ: 'abcde'[1:-2][::-1] cắt một char từ bên trái, hai từ bên phải, sau đó đảo ngược. (Xem thêm reversed().)

Cắt chuỗi là giống nhau, trừ khi nó bình thường hóa các chỉ số âm và không bao giờ có thể đi ra ngoài chuỗi:

LÀM: Mã bên dưới có lỗi "không bao giờ đi ra ngoài chuỗi" khi abs (bước)> 1; tôi suy nghĩ Tôi vá nó là chính xác, nhưng thật khó hiểu.

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

Đừng lo lắng về is None chi tiết - chỉ cần nhớ rằng bỏ qua start và / hoặc stop luôn làm điều đúng để cung cấp cho bạn toàn bộ chuỗi.

Bình thường hóa các chỉ số âm đầu tiên cho phép bắt đầu và / hoặc dừng lại để tính từ đầu độc lập: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc' mặc dù range(1,-2) == []. Quá trình chuẩn hóa đôi khi được coi là "modulo độ dài" nhưng lưu ý rằng nó thêm chiều dài chỉ một lần: ví dụ: 'abcde'[-53:42] chỉ là toàn bộ chuỗi.


32
2018-03-29 10:15



Các this_is_how_slicing_works không giống với slice python. VÍ DỤ. [0, 1, 2][-5:3:3] sẽ nhận được [0] trong python, nhưng list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3)) nhận được 1]. - Eastsun
@Eastsun Rất tiếc, bạn đã đúng! Một trường hợp rõ ràng hơn: range(4)[-200:200:3] == [0, 3] nhưng list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]. Của tôi if 0 <= i < len(seq): là một nỗ lực để thực hiện "không bao giờ đi ra ngoài trình tự" chỉ đơn giản là nhưng là sai cho bước> 1. Tôi sẽ viết lại nó sau ngày hôm nay (với các bài kiểm tra). - Beni Cherniavsky-Paskin


Tôi sử dụng phương pháp "điểm chỉ số giữa các yếu tố" để tự suy nghĩ về nó, nhưng một cách mô tả nó đôi khi giúp người khác nhận được nó là:

mylist[X:Y]

X là chỉ mục của phần tử đầu tiên bạn muốn.
Y là chỉ số của phần tử đầu tiên bạn không muốn.


31
2018-02-06 21:16