Rozumienie notacji plasterków

Potrzebuję dobrego wyjaśnienia (referencje są plusem) na temat notacji Pythona's slice.

Dla mnie ta notacja potrzebuje trochę podnieść.

Wygląda na niezwykle potężny, ale nie całkiem mam głowę wokół niego.

Rozwiązanie

To naprawdę 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ż wartość step, która może być używana z każdym z powyższych:

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

Kluczowym punktem do zapamiętania jest to, że wartość :stop reprezentuje pierwszą wartość, która nie znajduje się w wybranym wycinku. Tak więc, różnica pomiędzy stop a start jest liczbą wybranych elementów (jeśli step wynosi 1, domyślnie).

Inną cechą jest to, że start lub stop może być liczbą ujemną, co oznacza, że liczy się od końca tablicy zamiast od jej początku. A 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, step moż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 łaskawy dla programisty, jeśli jest mniej elementów, niż się o nie prosi. Na przykład, jeśli poprosisz o a[:-2], a a zawiera tylko jeden element, otrzymasz pustą listę zamiast błędu. Czasami wolałbyś błąd, więc musisz być świadomy, że może się to zdarzyć.

Relacja do obiektu slice()

Operator krojenia [] jest w rzeczywistości używany w powyższym kodzie z obiektem slice() używając notacji : (która jest ważna tylko wewnątrz []), tzn:

a[start:stop:step]

jest równoważne z:

a[slice(start, stop, step)]

Obiekty slice również zachowują się nieco inaczej w zależności od liczby argumentów, podobnie jak w przypadku range(), tzn. obsługiwane są zarówno slice(stop) jak i slice(start, stop[, krok]). Aby pominąć specyfikację danego argumentu, można użyć None, tak więc np. a[start:] jest równoważne a[slice(start, None)] lub a[::-1] jest równoważne a[slice(None, None, -1)].

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

Komentarze (7)

Tutorial Pythona]1 mówi o tym (przewiń trochę w dół, aż dojdziesz do części o krojeniu).

Diagram ASCII art jest również pomocny w zapamiętaniu, jak działają plasterki:

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

Jednym ze sposobów zapamiętania jak działają plasterki jest myślenie o indeksach jako wskazujących między znakami, z lewą krawędzią pierwszego znaku oznaczoną numerem 0. Następnie prawa krawędź ostatniego znaku ciągu n znaków ma indeks n.

Komentarze (2)

Wyliczenie możliwości, na jakie pozwala gramatyka:

>>> 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żej niż high-1.

Jeśli stride jest ujemny, to kolejność jest nieco zmieniona, ponieważ odliczamy w dół:

>>> 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 krojenie (z przecinkami i elipsami) są w większości używane tylko przez specjalne struktury danych (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)'
Komentarze (3)