Zrozumienie notacji plastra

3281

Potrzebuję dobrego wyjaśnienia (odniesienia są plusem) na notacji plastra Pythona.

Dla mnie ta notacja wymaga nieco poprawienia.

Wygląda niezwykle potężnie, ale nie do końca rozumiem.

Szymon
źródło

Odpowiedzi:

4497

To naprawdę bardzo proste:

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

Istnieje również stepwartość, której można użyć z dowolnym z powyższych:

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

Kluczową kwestią do zapamiętania jest to, że :stopwartość reprezentuje pierwszą wartość, która nie znajduje się w wybranym wycinku. Różnica między stopi startto liczba wybranych elementów (jeśli stepdomyślnie jest to 1).

Drugą cechą jest to, że startalbo stopmoże być ujemna liczba, co oznacza, że liczy się od końca tablicy zamiast początku. Więc:

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

Podobnie stepmoże być liczbą ujemną:

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 jest miły dla programisty, jeśli jest mniej elementów niż prosisz. Na przykład, jeśli poprosisz o a[:-2]i azawiera tylko jeden element, zamiast błędu pojawi się pusta lista. Czasami wolisz błąd, więc musisz mieć świadomość, że może się to zdarzyć.

Związek z slice()przedmiotem

Operator krojenia []jest faktycznie używany w powyższym kodzie z slice()obiektem używającym :notacji (która jest poprawna tylko w obrębie []), tj .:

a[start:stop:step]

jest równa:

a[slice(start, stop, step)]

Obiekty plasterków również zachowują się nieco inaczej w zależności od liczby argumentów, podobnie jak range(), tj. Oba slice(stop)i slice(start, stop[, step])są obsługiwane. Aby pominąć określenie danego argumentu, można użyć None, aby np. Był a[start:]równoważny a[slice(start, None)]lub a[::-1]równoważny a[slice(None, None, -1)].

Podczas gdy :notacja na podstawie jest bardzo pomocna przy prostym krojeniu, jawne użycie slice()obiektów upraszcza programowe generowanie krojenia.

Greg Hewgill
źródło
122
Wycinanie wbudowanych typów zwraca kopię, ale nie jest to uniwersalne. W szczególności wycięcie tablic NumPy zwraca widok, który dzieli pamięć z oryginałem.
Beni Cherniavsky-Paskin
43
Jest to piękna odpowiedź z głosami, aby to udowodnić, ale brakuje jej jednej rzeczy: możesz zastąpić Nonedowolną pustą przestrzeń. Na przykład [None:None]wykonuje całą kopię. Jest to przydatne, gdy musisz określić koniec zakresu za pomocą zmiennej i musisz dołączyć ostatni element.
Mark Ransom
6
To, co naprawdę mnie denerwuje, to to, że python mówi, że kiedy nie ustawisz początku i końca, domyślnie będą to 0 i długość sekwencji. Teoretycznie więc, gdy użyjesz „abcdef” [:: - 1], należy go przekształcić w „abcdef” [0: 6: -1], ale te dwa wyrażenia nie uzyskują tego samego wyniku. Czuję, że brakuje czegoś w dokumentacji Pythona od czasu stworzenia języka.
axell13
6
I wiem, że „abcdef” [:: - 1] jest przekształcane na „abcdef” [6: -7: -1], więc najlepszym sposobem wyjaśnienia byłoby: niech len będzie długością sekwencji. Jeśli krok jest dodatni , wartości domyślne dla początku i końca to 0 i len . W przeciwnym razie, jeśli krok jest ujemny , wartości domyślne dla początku i końca to len i - len - 1.
axell13
4
Skoro stackoverflow.com/questions/39241529/... jest oznaczony jako duplikat tego, czy warto dodać sekcję zawierającą delnotację wycinania wrt. W szczególności del arr[:]nie jest od razu oczywiste („arr [:] tworzy kopię, więc del usuwa tę kopię ???” itd.)
Khazhyk,
537

W Python Tutorial mówi o nim (w dół trochę, aż dojdziesz do części o krojenie).

Schemat artystyczny ASCII jest również pomocny do zapamiętania, jak działają plastry:

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

Jednym ze sposobów na zapamiętanie działania wycinków jest myślenie o wskaźnikach wskazujących między znakami, z lewą krawędzią pierwszego znaku ponumerowaną 0. Następnie prawa krawędź ostatniego znaku ciągu n znaków ma indeks n .

Hans Nowak
źródło
10
Ta sugestia działa dla kroku dodatniego, ale nie dla kroku ujemnego. Z diagramu oczekuję, że a[-4,-6,-1]tak będzie, yPale tak jest ty. To, co zawsze działa, to myślenie w postaciach lub slotach i stosowanie indeksowania jako półotwartego przedziału - otwarcie prawe, jeśli krok dodatni, otwarcie lewe, jeśli krok ujemny.
aguadopd
Ale nie ma sposobu, aby zwinąć do pustego zestawu, zaczynając od końca (podobnie jak w x[:0]przypadku rozpoczynania od początku), więc musisz specjalne małe tablice. : /
endolith,
412

Wymieniając możliwości dozwolone przez gramatykę:

>>> 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]]

Oczywiście, jeśli (high-low)%stride != 0, to punkt końcowy będzie nieco niższy niż high-1.

Jeśli stridejest ujemne, kolejność jest nieco zmieniona, ponieważ odliczamy:

>>> 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]]

Rozszerzone segmentowanie (z przecinkami i elipsami) jest najczęściej używane tylko przez specjalne struktury danych (takie jak NumPy); podstawowe sekwencje ich nie obsługują.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
efemeryczny
źródło
Właściwie wciąż coś jest pomijane, np. Jeśli napiszę „jabłko” [4: -4: -1] Dostaję „elp”, python tłumaczy wartość -4 na 1?
liyuan
zauważ, że backticks są przestarzałe na korzyśćrepr
wjandrea
@liyuan Typem implementacji __getitem__jest; twój przykład jest równoważny z apple[slice(4, -4, -1)].
chepner
320

Odpowiedzi powyżej nie omawiają przypisania plasterków. Aby zrozumieć przypisanie plasterka, pomocne jest dodanie innej koncepcji do grafiki ASCII:

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

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

Jedna heurystyka polega na tym, że dla wycinka od zera do n pomyśl: „zero to początek, zacznij od początku i weź n elementów z listy”.

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

Inna heurystyka to „w przypadku dowolnego wycinka zastąp początkową wartość zerową, zastosuj poprzednią heurystykę, aby uzyskać koniec listy, a następnie policz pierwszą liczbę z powrotem, aby odciąć elementy od początku”

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

Pierwsza zasada przypisywania wycinka polega na tym, że ponieważ wycinanie zwraca listę, przypisanie wycinka wymaga listy (lub innej iterowalnej):

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

Drugą zasadą przypisywania wycinka, którą można również zobaczyć powyżej, jest to, że cokolwiek część listy jest zwracana przez indeksowanie wycinków, to ta sama część, która jest zmieniana przez przypisanie wycinków:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

Trzecią zasadą przypisywania wycinka jest to, że przypisana lista (iterowalna) nie musi mieć tej samej długości; indeksowany plasterek jest po prostu wycinany i zastępowany masowo przez to, co jest przypisane:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

Najtrudniejszą częścią, do której należy się przyzwyczaić, jest przydzielanie pustych plasterków. Za pomocą heurystyki 1 i 2 łatwo jest zaindeksować indeksowanie pustego wycinka:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

A kiedy już to zobaczysz, przypisanie wycinka do pustego wycinka również ma sens:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

Zauważ, że ponieważ nie zmieniamy drugiej liczby wycinka (4), wstawiane elementy zawsze układają się w stos względem „o”, nawet gdy przypisujemy do pustego wycinka. Zatem pozycja dla przypisania pustego wycinka jest logicznym rozszerzeniem pozycji dla niepustych przypisań wycinka.

Cofając się trochę, co się stanie, gdy będziesz kontynuował naszą procesję liczenia początku plastra?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

Z krojeniem, kiedy skończysz, skończysz; nie zaczyna kroić wstecz. W Pythonie nie otrzymujesz ujemnych kroków, chyba że wyraźnie poprosisz o nie, używając liczby ujemnej.

>>> p[5:3:-1]
 ['n','o']

Istnieją dziwne konsekwencje dla zasady „kiedy skończysz, skończysz”:

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

W rzeczywistości, w porównaniu do indeksowania, krojenie w Pythonie jest dziwnie odporne na błędy:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

Czasami może się to przydać, ale może również prowadzić do nieco dziwnych zachowań:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

W zależności od aplikacji może to ... lub nie ... być tym, na co liczyłeś!


Poniżej znajduje się tekst mojej oryginalnej odpowiedzi. Przydał się wielu osobom, więc nie chciałem go usuwać.

>>> 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]

Może to również wyjaśnić różnicę między krojeniem a indeksowaniem.

David M. Perlman
źródło
246

Wyjaśnij notację plastra Pythona

W skrócie, dwukropek ( :) w notacji indeksem ( subscriptable[subscriptarg]) make plaster notacja - który ma opcjonalnych argumentów start, stop, step:

sliceable[start:stop:step]

Wycinanie w języku Python jest obliczeniowo szybkim sposobem na metodyczny dostęp do części danych. Moim zdaniem, aby być nawet pośrednim programistą Python, jest to jeden z aspektów języka, który trzeba znać.

Ważne definicje

Na początek zdefiniujmy kilka terminów:

start: indeks początkowy wycinka, będzie zawierał element o tym indeksie, chyba że jest taki sam jak stop , domyślnie 0, tzn. pierwszy indeks. Jeśli jest ujemny, oznacza to, że zaczynasz nprzedmioty od końca.

stop: indeks końcowy wycinka, nie zawiera elementu o tym indeksie, domyślnie długość odcinki jest wycinana, to znaczy do końca włącznie.

krok: kwota, o którą indeks rośnie, domyślnie wynosi 1. Jeśli jest ujemny, przecinasz iterację w odwrotnej kolejności.

Jak działa indeksowanie

Możesz wykonać dowolną z tych liczb dodatnich lub ujemnych. Znaczenie liczb dodatnich jest prosta, ale dla liczb ujemnych, podobnie jak indeksy w Pythonie, można liczyć wstecz od końca do początku i zatrzymania , a po kroku , po prostu zmniejszyć swój indeks. Ten przykład pochodzi z samouczka dokumentacji , ale nieco go zmodyfikowałem, aby wskazać, który element w sekwencji odnosi się do każdego indeksu:

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

Jak działa krojenie

Aby użyć notacji wycinka z sekwencją, która ją obsługuje, musisz umieścić co najmniej jeden dwukropek w nawiasach kwadratowych następujących po sekwencji (które faktycznie implementują __getitem__metodę sekwencji, zgodnie z modelem danych Python ).

Notacja wycinka działa w następujący sposób:

sequence[start:stop:step]

I pamiętaj, że istnieją wartości domyślne dla startu , stopu i kroku , więc aby uzyskać dostęp do wartości domyślnych, po prostu pomiń argument.

Notacja plastra, aby uzyskać ostatnie dziewięć elementów z listy (lub dowolnej innej sekwencji, która ją obsługuje, np. Ciąg znaków) wyglądałaby następująco:

my_list[-9:]

Kiedy to widzę, czytam tę część w nawiasach jako „9. od końca do końca”. (Właściwie to nazywam to mentalnie jako „-9, on”)

Wyjaśnienie:

Pełna notacja to

my_list[-9:None:None]

i aby zastąpić wartości domyślne (w rzeczywistości, gdy wartość stepjest ujemna, stopwartość domyślna to -len(my_list) - 1, więc Nonedla stop naprawdę oznacza to, że przechodzi do dowolnego kroku końcowego, do którego prowadzi):

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

Okrężnicy , :jest to, co mówi Python dajesz mu kawałek, a nie regularne indeksu. Właśnie dlatego idiomatycznym sposobem tworzenia płytkiej kopii list w Pythonie 2 jest

list_copy = sequence[:]

I ich usunięcie polega na:

del my_list[:]

(Python 3 otrzymuje metodę list.copya list.clear).

Kiedy stepjest ujemne, wartości domyślne starti stopzmieniają się

Domyślnie, gdy stepargument jest pusty (lub None), zostaje przypisany do +1.

Ale możesz podać ujemną liczbę całkowitą, a lista (lub większość innych standardowych slicabli) zostanie pocięta od końca do początku.

Tak więc negatywny plasterek zmieni wartości domyślne dla starti stop!

Potwierdzenie tego w źródle

Chciałbym zachęcić użytkowników do zapoznania się ze źródłem oraz dokumentacją. Kod źródłowy wycinka obiektów i ta logika jest znaleźć tutaj . Najpierw określamy, czy stepjest ujemny:

 step_is_negative = step_sign < 0;

Jeśli tak, dolna granica -1 oznacza, że ​​przecinamy aż do początku włącznie, a górna granica to długość minus 1, co oznacza, że ​​zaczynamy od końca. (Należy pamiętać, że semantyka ta -1jest różna od A -1czemu użytkownicy mogą przechodzić indeksów w Pythonie wskazujące ostatni element).

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

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

W przeciwnym razie stepjest dodatnia, a dolna granica będzie wynosić zero, a górna granica (do której idziemy do góry, ale nie obejmuje) długości podzielonej listy.

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

Następnie może być konieczne zastosowanie wartości domyślnych dla starti stop- wartość domyślna wtedy dla startjest obliczana jako górna granica, gdy stepjest ujemna:

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

i stopdolna granica:

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

Nadaj swoim plasterkom opisową nazwę!

Przydatne może być oddzielenie formowania wycinka od przekazania go do list.__getitem__metody ( to właśnie robią nawiasy kwadratowe ). Nawet jeśli nie jesteś nowy, kod jest bardziej czytelny, dzięki czemu inni, którzy mogą przeczytać kod, mogą łatwiej zrozumieć, co robisz.

Nie można jednak przypisać zmiennej do liczb całkowitych oddzielonych dwukropkami. Musisz użyć obiektu plasterka:

last_nine_slice = slice(-9, None)

Drugi argument, Nonejest wymagany, aby pierwszy argument był interpretowany jako startargument, w przeciwnym razie byłby to stopargument .

Następnie możesz przekazać obiekt plasterka do swojej sekwencji:

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

Interesujące jest to, że zakresy również biorą plasterki:

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

Uwagi dotyczące pamięci:

Ponieważ wycinki list w języku Python tworzą nowe obiekty w pamięci, kolejną ważną funkcją, o której należy pamiętać, jest itertools.islice. Zazwyczaj będziesz chciał iterować fragment, a nie tylko utworzyć go statycznie w pamięci. islicejest do tego idealny. Zastrzeżenie, nie obsługuje negatywnych argumentów na rzecz start, stoplub step, jeśli jest to problem, może być konieczne obliczenie indeksów lub odwrócenie iterowalności z góry.

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

i teraz:

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

Fakt, że wycinki list tworzą kopię, jest cechą samych list. Jeśli kroisz zaawansowane obiekty, takie jak Pandas DataFrame, może zwrócić widok oryginału, a nie jego kopię.

Aaron Hall
źródło
145

I kilka rzeczy, które nie były od razu dla mnie oczywiste, kiedy po raz pierwszy zobaczyłem składnię krojenia:

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

Łatwy sposób na odwrócenie sekwencji!

A jeśli z jakiegoś powodu chciałeś, co drugi element w odwrotnej kolejności:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]
Dana
źródło
100

W Pythonie 2.7

Krojenie w Pythonie

[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).

Zrozumienie przypisania indeksu jest bardzo ważne.

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

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

Kiedy mówisz [a: b: c], mówisz w zależności od znaku c (do przodu lub do tyłu), zacznij od a, a zakończ na b (wyłączając element przy bth index). Użyj powyższej reguły indeksowania i pamiętaj, że znajdziesz tylko elementy z tego zakresu:

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

Ale zasięg ten jest kontynuowany w obu kierunkach w nieskończoność:

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

Na przykład:

             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

Jeśli twój wybór a, b i c pozwala na pokrywanie się z powyższym zakresem podczas przechodzenia za pomocą reguł dla a, b, c powyżej, albo otrzymasz listę z elementami (dotkniętymi podczas przechodzenia), albo dostaniesz pustą listę.

I ostatnia rzecz: jeśli aib są równe, to również otrzymujesz pustą listę:

>>> 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]
Ankur Agarwal
źródło
2
kolejny interesujący przykład: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]który prowadzi do[9]
Deviacium
96

Znalazłem ten wielki stół na 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)
AdrianoFerrari
źródło
65

Po odrobinie użycia zdaję sobie sprawę, że najprostszym opisem jest to, że jest dokładnie taki sam jak argumenty w forpętli ...

(from:to:step)

Każda z nich jest opcjonalna:

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

Następnie indeksowanie ujemne wymaga tylko dodania długości łańcucha do indeksów ujemnych, aby go zrozumieć.

W każdym razie to działa dla mnie ...

Szymon
źródło
52

Łatwiej jest mi zapamiętać, jak to działa, a potem mogę wymyślić dowolną kombinację start / stop / krok.

Warto range()najpierw zrozumieć :

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

Zacznij od start, zwiększaj step, nie sięgaj stop. Bardzo prosta.

O kroku ujemnym należy pamiętać, że stopzawsze jest to koniec wykluczony, niezależnie od tego, czy jest wyższy czy niższy. Jeśli chcesz tego samego plasterka w odwrotnej kolejności, znacznie łatwiej jest wykonać odwrócenie osobno: np. 'abcde'[1:-2][::-1]Odciąć jeden znak od lewej, dwa od prawej, a następnie cofa. (Zobacz także reversed().)

Krojenie sekwencji jest takie samo, z tym wyjątkiem, że najpierw normalizuje indeksy ujemne i nigdy nie może wyjść poza sekwencję:

DO ZROBIENIA : Poniższy kod zawiera błąd „nigdy nie wychodź poza sekwencję”, gdy abs (krok)> 1; Myślę , że załatałem to, żeby było poprawne, ale trudno to zrozumieć.

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]

Nie martw się o is Noneszczegóły - pamiętaj tylko, że pominięcie starti / lub stopzawsze robi to dobrze, aby dać ci całą sekwencję.

Normalizacja ujemnych indeksów najpierw pozwala na liczenie startu i / lub zatrzymania od końca niezależnie: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc'pomimo range(1,-2) == []. Normalizacja jest czasem uważana za „modulo długość”, ale należy pamiętać, że dodaje długość tylko raz: np. 'abcde'[-53:42]Jest to cały ciąg.

Beni Cherniavsky-Paskin
źródło
3
To this_is_how_slicing_worksnie to samo co plasterek Pythona. EG [0, 1, 2][-5:3:3]otrzyma [0] w pythonie, ale list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))dostanie [1].
Eastsun
@Eastsun Ups, masz rację! Jaśniejszy przypadek: range(4)[-200:200:3] == [0, 3]ale list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]. Moja if 0 <= i < len(seq):próba wdrożenia po prostu „nigdy nie wychodź poza sekwencję”, ale jest błędna dla kroku> 1. Przepiszę go później dzisiaj (z testami).
Beni Cherniavsky-Paskin
40

Sam o tym myślę, używając metody „indeksu punktów między elementami”, ale jednym ze sposobów jej opisania, który czasem pomaga innym, jest:

mylist[X:Y]

X jest indeksem pierwszego pożądanego elementu.
Y jest indeksem pierwszego elementu, którego nie chcesz.

Steve Losh
źródło
40
Index:
      ------------>
  0   1   2   3   4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
  0  -4  -3  -2  -1
      <------------

Slice:
    <---------------|
|--------------->
:   1   2   3   4   :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
:  -4  -3  -2  -1   :
|--------------->
    <---------------|

Mam nadzieję, że pomoże to w modelowaniu listy w Pythonie.

Odniesienie: http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

xiaoyu
źródło
38

Notacja krojenia w języku Python:

a[start:end:step]
  • Dla starti endujemne wartości są interpretowane jako odnoszące się do końca sekwencji.
  • Pozytywne wskaźniki endwskazują pozycję po ostatnim uwzględnionym elemencie.
  • Puste wartości są domyślnie w następujący sposób: [+0:-0:1].
  • Użycie kroku ujemnego odwraca interpretację startiend

Notacja rozciąga się na (liczbowe) macierze i tablice wielowymiarowe. Na przykład, aby pokroić całe kolumny, możesz użyć:

m[::,0:2:] ## slice the first two columns

Plasterki zawierają odniesienia, a nie kopie elementów tablicy. Jeśli chcesz zrobić oddzielną kopię tablicy, możesz użyć deepcopy().

nobar
źródło
34

Możesz także użyć przypisania plasterka, aby usunąć jeden lub więcej elementów z listy:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
dansalmo
źródło
33

To tylko dodatkowe informacje ... Rozważ poniższą listę

>>> l=[12,23,345,456,67,7,945,467]

Kilka innych sztuczek do odwracania listy:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
Arindam Roychowdhury
źródło
33

Oto jak uczę początkujących plasterków:

Zrozumienie różnicy między indeksowaniem a krojeniem:

Wiki Python ma ten niesamowity obraz, który wyraźnie odróżnia indeksowanie i krojenie.

Wpisz opis zdjęcia tutaj

Jest to lista z sześcioma elementami. Aby lepiej zrozumieć krojenie, rozważ tę listę jako zestaw sześciu pudełek razem. Każde pudełko ma alfabet.

Indeksowanie przypomina postępowanie z zawartością ramki. Możesz sprawdzić zawartość dowolnego pola. Ale nie można sprawdzić zawartości wielu pól jednocześnie. Możesz nawet wymienić zawartość pudełka. Ale nie możesz umieścić dwóch piłek w jednym pudełku ani zastąpić dwóch piłek jednocześnie.

In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']

In [124]: alpha[0]
Out[124]: 'a'

In [127]: alpha[0] = 'A'

In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']

In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]

TypeError: list indices must be integers, not tuple

Krojenie jest jak radzenie sobie z pudełkami. Możesz podnieść pierwsze pudełko i umieścić je na innym stole. Aby podnieść skrzynkę, wszystko, co musisz wiedzieć, to pozycja początku i końca skrzynki.

Możesz nawet odebrać pierwsze trzy pola lub ostatnie dwa pola lub wszystkie pola od 1 do 4. Możesz więc wybrać dowolny zestaw pól, jeśli znasz początek i koniec. Pozycje te nazywane są pozycjami start i stop.

Interesujące jest to, że możesz zastąpić wiele pól jednocześnie. Możesz także umieścić wiele pól w dowolnym miejscu.

In [130]: alpha[0:1]
Out[130]: ['A']

In [131]: alpha[0:1] = 'a'

In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']

In [133]: alpha[0:2] = ['A', 'B']

In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']

In [135]: alpha[2:2] = ['x', 'xx']

In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']

Krojenie za pomocą kroku:

Do tej pory ciągle wybierałeś pudełka. Ale czasami musisz odebrać dyskretnie. Na przykład możesz odebrać co drugie pudełko. Możesz nawet odebrać co trzecie pudełko od końca. Ta wartość nazywa się wielkością kroku. To reprezentuje lukę między kolejnymi przetwornikami. Rozmiar kroku powinien być dodatni, jeśli wybierasz pudełka od początku do końca i odwrotnie.

In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']

In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']

In [144]: alpha[1:5:-2]
Out[144]: []

In [145]: alpha[-1:-5:2]
Out[145]: []

Jak Python rozpoznaje brakujące parametry:

Podczas krojenia, jeśli pominiesz jakiś parametr, Python spróbuje go rozgryźć automatycznie.

Jeśli sprawdzisz kod źródłowy CPython , znajdziesz funkcję o nazwie PySlice_GetIndicesEx (), która oblicza wskaźniki dla wycinka dla dowolnych podanych parametrów. Oto logiczny równoważny kod w Pythonie.

Ta funkcja pobiera obiekt Python i opcjonalne parametry do krojenia i zwraca długość początku, zatrzymania, kroku i długości żądanego plasterka.

def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):

    length = len(obj)

    if step is None:
        step = 1
    if step == 0:
        raise Exception("Step cannot be zero.")

    if start is None:
        start = 0 if step > 0 else length - 1
    else:
        if start < 0:
            start += length
        if start < 0:
            start = 0 if step > 0 else -1
        if start >= length:
            start = length if step > 0 else length - 1

    if stop is None:
        stop = length if step > 0 else -1
    else:
        if stop < 0:
            stop += length
        if stop < 0:
            stop = 0 if step > 0 else -1
        if stop >= length:
            stop = length if step > 0 else length - 1

    if (step < 0 and stop >= start) or (step > 0 and start >= stop):
        slice_length = 0
    elif step < 0:
        slice_length = (stop - start + 1)/(step) + 1
    else:
        slice_length = (stop - start - 1)/(step) + 1

    return (start, stop, step, slice_length)

To inteligencja obecna za plasterkami. Ponieważ Python ma wbudowaną funkcję o nazwie plasterek, możesz przekazać niektóre parametry i sprawdzić, jak mądrze oblicza brakujące parametry.

In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [22]: s = slice(None, None, None)

In [23]: s
Out[23]: slice(None, None, None)

In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)

In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]

In [26]: s = slice(None, None, -1)

In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]

In [28]: s = slice(None, 3, -1)

In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]

Uwaga: ten post został pierwotnie napisany na moim blogu, The Intelligence Behind Python Slices .

ChillarAnand
źródło
29

Zasadą jest, że pisanie kodu z wieloma zakodowanymi wartościami indeksu prowadzi do bałaganu w zakresie czytelności i konserwacji. Na przykład, jeśli wrócisz do kodu rok później, popatrzysz na niego i zastanawiasz się, co myślisz, kiedy go napisałeś. Przedstawione rozwiązanie jest po prostu sposobem na wyraźniejsze określenie, co faktycznie robi Twój kod. Ogólnie rzecz biorąc, wbudowany slice () tworzy obiekt plasterka, którego można używać wszędzie tam, gdzie dozwolony jest kawałek. Na przykład:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

Jeśli masz instancję wycinka, możesz uzyskać więcej informacji na ten temat, patrząc odpowiednio na jego atrybuty s.start, s.stop i s.step. Na przykład:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>
Python_Dude
źródło
25

1. Notacja plastra

Upraszczając, pamiętaj, że plasterek ma tylko jedną formę form

s[start:end:step]

a oto jak to działa:

  • s: obiekt, który można pokroić
  • start: pierwszy indeks, aby rozpocząć iterację
  • end: ostatni indeks, UWAGA, że endindeks nie zostanie uwzględniony w wynikowym wycinku
  • step: wybierz element w każdym stepindeksie

Kolejny import rzecz: wszyscy start, end, stepmożna pominąć! A jeśli są one pomijane, ich wartość zostanie użyta domyślna: 0, len(s), 1odpowiednio.

Możliwe warianty to:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

UWAGA: Jeśli start >= end(rozważając tylko kiedy step>0), Python zwróci pusty plasterek [].

2. Pułapki

W powyższej części wyjaśniono podstawowe funkcje działania wycinka i zadziała ono w większości przypadków. Mogą jednak wystąpić pułapki, na które należy uważać, a ta część wyjaśnia je.

Wskaźniki ujemne

Pierwszą rzeczą, która dezorientuje uczących się języka Python, jest to, że indeks może być ujemny! Nie panikuj: ujemny indeks oznacza liczyć wstecz.

Na przykład:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

Krok negatywny

Sprawianie, że rzeczy są bardziej mylące, stepmoże być również negatywne!

Krok ujemny oznacza iterację tablicy do tyłu: od końca do początku, z włączonym indeksem końcowym i indeksem początkowym wykluczonym z wyniku.

UWAGA : gdy krok jest ujemny, domyślną wartością startjest len(s)(chociaż endnie jest równy 0, ponieważ s[::-1]zawiera s[0]). Na przykład:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

Błąd poza zakresem?

Zaskocz się: wycinek nie podnosi błędu indeksu, gdy indeks jest poza zakresem!

Jeśli indeks jest poza zakresem, Python dołoży wszelkich starań, aby ustawić indeks na 0lub len(s)zgodnie z sytuacją. Na przykład:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3. Przykłady

Zakończmy tę odpowiedź przykładami, wyjaśniając wszystko, co omówiliśmy:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range
cizixs
źródło
24

Poprzednie odpowiedzi nie omawiają wielowymiarowego dzielenia tablic, co jest możliwe przy użyciu słynnego pakietu NumPy :

Krojenie można również zastosować do tablic wielowymiarowych.

# Here, a is a NumPy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
       [5, 7]])

:2” Przed przecinkiem operuje na pierwszym wymiarze, a „ 0:3:2” po przecinku na drugim wymiarze.

Statham
źródło
4
Tylko przyjacielskie przypomnienie, że nie można tego zrobić na Pythonie, listale tylko arrayw Numpy
Mars Lee
15
#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

Możesz uruchomić ten skrypt i eksperymentować z nim, poniżej jest kilka próbek, które otrzymałem ze skryptu.

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

W przypadku kroku ujemnego zwróć uwagę, że odpowiedź jest przesunięta w prawo o 1.

mahmoh
źródło
14

Mój mózg wydaje się szczęśliwy, że lst[start:end]zawiera start-ty element. Mógłbym nawet powiedzieć, że jest to „naturalne założenie”.

Ale czasami pojawiają się wątpliwości i mój mózg prosi o zapewnienie, że nie zawiera on-tego endelementu.

W tych momentach opieram się na tym prostym twierdzeniu:

for any n,    lst = lst[:n] + lst[n:]

Ta ładna właściwość mówi mi, że lst[start:end]nie zawiera end-tego elementu, ponieważ jest w lst[end:].

Zauważ, że to twierdzenie jest prawdziwe dla każdego n. Na przykład możesz to sprawdzić

lst = range(10)
lst[:-42] + lst[-42:] == lst

zwraca True.

Robert
źródło
12

Moim zdaniem lepiej zrozumiesz i zapamiętasz notację krojenia ciągów znaków w Pythonie, jeśli spojrzysz na nią w następujący sposób (czytaj dalej).

Pracujmy z następującym ciągiem ...

azString = "abcdefghijklmnopqrstuvwxyz"

Dla tych, którzy nie wiedzą, możesz utworzyć dowolny podciąg azStringza pomocą notacjiazString[x:y]

Pochodząc z innych języków programowania, wtedy zdrowy rozsądek zostaje naruszony. Co to są xiy?

Musiałem usiąść i uruchomić kilka scenariuszy, szukając techniki zapamiętywania, która pomoże mi zapamiętać, czym są xiy, i pomoże mi prawidłowo przeciąć łańcuchy przy pierwszej próbie.

Mój wniosek jest taki, że xiy należy postrzegać jako indeksy brzegowe otaczające ciągi, które chcemy uzupełnić. Powinniśmy więc postrzegać to wyrażenie jako azString[index1, index2]lub nawet bardziej wyraźne jako azString[index_of_first_character, index_after_the_last_character].

Oto przykładowa wizualizacja tego ...

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

Wszystko, co musisz zrobić, to ustawić indeks1 i indeks2 na wartości, które będą otaczały pożądany podciąg. Na przykład, aby uzyskać podciąg „cdefgh”, możesz użyć azString[2:8], ponieważ indeks po lewej stronie „c” wynosi 2, a indeks po prawej stronie „h” to 8.

Pamiętaj, że wyznaczamy granice. A tymi granicami są pozycje, w których można umieścić nawiasy klamrowe, które zostaną owinięte wokół podciągu w ten sposób ...

ab [ cdefgh ] ij

Ta sztuczka działa cały czas i łatwo ją zapamiętać.

asiby
źródło
11

Większość poprzednich odpowiedzi wyjaśnia pytania dotyczące notacji plastra.

Rozszerzona składnia indeksowania używana do krojenia jest aList[start:stop:step], a podstawowe przykłady to:

Wpisz opis zdjęcia tutaj:

Więcej przykładów krojenia: 15 rozszerzonych krojów

Roshan Bagdiya
źródło
10

W Pythonie najbardziej podstawową formą krojenia jest:

l[start:end]

gdzie ljest jakaś kolekcja, startjest indeksem włączającym i endjest indeksem wyłącznym.

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

Podczas krojenia od samego początku można pominąć indeks zerowy, a podczas krojenia do końca można pominąć indeks końcowy, ponieważ jest on zbędny, więc nie należy powtarzać:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

Ujemne liczby całkowite są użyteczne podczas wykonywania przesunięć względem końca kolekcji:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

Podczas krojenia można podać wskaźniki, które są poza zakresem, takie jak:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Pamiętaj, że wynikiem krojenia kolekcji jest zupełnie nowa kolekcja. Ponadto w przypadku przypisania wycinka w przypisaniach długość przypisań wycinka nie musi być taka sama. Wartości przed i po przypisanym wycinku zostaną zachowane, a kolekcja zmniejszy się lub powiększy, aby zawierać nowe wartości:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

Jeśli pominiesz indeks początkowy i końcowy, utworzysz kopię kolekcji:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

Jeśli indeksy początkowy i końcowy zostaną pominięte podczas wykonywania operacji przypisania, cała zawartość kolekcji zostanie zastąpiona kopią tego, do którego się odwołuje:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

Oprócz podstawowego krojenia można również zastosować następującą notację:

l[start:end:step]

gdzie ljest kolekcją, startjest indeksem włączającym, endjest indeksem wyłącznym i stepjest krokiem, którego można użyć, aby wziąć co n-ty przedmiot l.

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

Użycie stepzapewnia przydatną sztuczkę do odwrócenia kolekcji w Pythonie:

In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Możliwe jest również użycie ujemnych liczb całkowitych dla stepnastępującego przykładu:

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

Jednak użycie wartości ujemnej dla stepmoże stać się bardzo mylące. Ponadto, aby być pythonic , należy unikać start, endoraz stepw pojedynczym segmencie. Jeśli jest to wymagane, zastanów się nad zrobieniem tego w dwóch zadaniach (jednym do pokrojenia, a drugim do kroku).

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]
lmiguelvargasf
źródło
10

Chcę dodać jedno Witaj, świecie! przykład wyjaśniający podstawy plasterków dla początkujących. Bardzo mi pomogło.

Zróbmy listę z sześcioma wartościami ['P', 'Y', 'T', 'H', 'O', 'N']:

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5

Teraz najprostszymi plasterkami tej listy są podlisty. Notacja jest, [<index>:<index>]a kluczem jest odczytanie jej w następujący sposób:

[ start cutting before this index : end cutting before this index ]

Teraz, jeśli utworzysz kawałek [2:5]powyższej listy, stanie się to:

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5

Wykonałeś cięcie przed elementem z indeksem 2i kolejne cięcie przed elementem z indeksem 5. Tak więc wynikiem będzie plasterek między tymi dwoma cięciami, lista ['T', 'H', 'O'].

Jeyekomon
źródło
10

Osobiście myślę o tym jak o forpętli:

a[start:end:step]
# for(i = start; i < end; i += step)

Zauważ też, że ujemne wartości dla starti endsą względem końca listy i obliczone w powyższym przykładzie przez given_index + a.shape[0].

Raman
źródło
8

Poniżej znajduje się przykład indeksu ciągu:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Przykład krojenia: [start: koniec: krok]

str[start:end] # Items start through end-1
str[start:]    # Items start through the rest of the array
str[:end]      # Items from the beginning through end-1
str[:]         # A copy of the whole array

Poniżej znajduje się przykładowe użycie:

print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti
Książę Dhadwal
źródło
5

Jeśli uważasz, że ujemne wskaźniki w krojeniu są mylące, oto bardzo prosty sposób, aby o tym pomyśleć: po prostu zastąp indeks ujemny przez len - index. Na przykład zamień -3 na len(list) - 3.

Najlepszym sposobem zilustrowania wewnętrznego podziału na plastry jest po prostu pokazanie go w kodzie implementującym tę operację:

def slice(list, start = None, end = None, step = 1):
  # Take care of missing start/end parameters
  start = 0 if start is None else start
  end = len(list) if end is None else end

  # Take care of negative start/end parameters
  start = len(list) + start if start < 0 else start
  end = len(list) + end if end < 0 else end

  # Now just execute a for-loop with start, end and step
  return [list[i] for i in range(start, end, step)]
Shital Shah
źródło
4

Podstawową techniką krojenia jest określenie punktu początkowego, punktu zatrzymania i wielkości kroku - znanej również jako krok.

Najpierw stworzymy listę wartości do wykorzystania w naszym krojeniu.

Utwórz dwie listy do wycięcia. Pierwsza to lista numeryczna od 1 do 9 (Lista A). Druga to także lista numeryczna od 0 do 9 (Lista B):

A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))

print("This is List A:", A)
print("This is List B:", B)

Zindeksuj liczbę 3 od A i liczbę 6 od B.

print(A[2])
print(B[6])

Podstawowe krojenie

Rozszerzona składnia indeksowania używana do krojenia to aList [start: stop: step]. Argument początkowy i krokowy domyślnie nie ma wartości - jedynym wymaganym argumentem jest stop. Czy zauważyłeś, że jest to podobne do tego, w jaki sposób użyto zakresu do zdefiniowania list A i B? Wynika to z faktu, że obiekt wycinka reprezentuje zestaw wskaźników określonych przez zakres (początek, stop, krok). Dokumentacja Python 3.4.

Jak widać, zdefiniowanie tylko stop zwraca jeden element. Ponieważ domyślnym początkiem jest brak, przekłada się to na pobranie tylko jednego elementu.

Należy zauważyć, że pierwszym elementem jest indeks 0, a nie indeks 1. Dlatego do tego ćwiczenia używamy 2 list. Elementy listy A są ponumerowane zgodnie z pozycją porządkową (pierwszy element to 1, drugi element to 2 itd.), Natomiast elementy listy B to liczby, które zostałyby użyte do ich indeksowania ([0] dla pierwszego elementu 0, itp.).

Dzięki rozszerzonej składni indeksowania pobieramy zakres wartości. Na przykład wszystkie wartości są pobierane za pomocą dwukropka.

A[:]

Aby pobrać podzbiór elementów, należy zdefiniować pozycje początkową i końcową.

Biorąc pod uwagę wzorzec aList [start: stop], pobierz pierwsze dwa elementy z Listy A.

Babu Chandermani
źródło
3

Nie sądzę, że schemat samouczka w języku Python (cytowany w różnych innych odpowiedziach) jest dobry, ponieważ ta sugestia działa pozytywnie, ale nie działa negatywnie.

Oto schemat:

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

Z diagramu oczekuję, że a[-4,-6,-1]tak będzie, yPale tak jest ty.

>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'

To, co zawsze działa, to myślenie w postaciach lub slotach i stosowanie indeksowania jako półotwartego przedziału - otwarcie prawe, jeśli krok dodatni, otwarcie lewe, jeśli krok ujemny.

W ten sposób mogę myśleć a[-4:-6:-1], jak a(-6,-4]w przedziale terminologii.

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

 +---+---+---+---+---+---+---+---+---+---+---+---+
 | P | y | t | h | o | n | P | y | t | h | o | n |
 +---+---+---+---+---+---+---+---+---+---+---+---+
  -6  -5  -4  -3  -2  -1   0   1   2   3   4   5  
aguadopd
źródło