Pytanie Zrozumienie notacji plasterka Pythona


Potrzebuję dobrego wyjaśnienia (odniesienia to plus) w notacji Pythona.

Dla mnie ta notacja wymaga trochę podnoszenia.

Wygląda niesamowicie potężnie, ale nie bardzo to rozumiałem.


2300
2018-02-03 22:31


pochodzenie




Odpowiedzi:


To naprawdę proste:

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

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

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

Kluczowym punktem do zapamiętania jest to, że :end wartość reprezentuje pierwszą wartość nie w wybranym plasterku. Różnica między nimi end i start to liczba wybranych elementów (jeśli step jest 1, domyślnie).

Inną cechą jest to start lub end może negatywny liczba, co oznacza, że ​​liczy się od końca tablicy, a nie od 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, 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 miły dla programisty, jeśli jest mniej przedmiotów niż pytasz. Na przykład, jeśli o to poprosisz a[:-2] i a zawiera tylko jeden element, zamiast błędu pojawia się pusta lista. Czasami wolisz błąd, więc musisz zdawać sobie sprawę, że tak się może stać.


3112
2018-02-03 22:48



Wycięte typy wbudowane zwracają kopię, ale to nie jest uniwersalne. Szczególnie, krojenie tablic NumPy zwraca widok współużytkujący pamięć z oryginałem. - Beni Cherniavsky-Paskin
Co by to zrobiło? [::-2]  Próbowałem, ale nie rozumiem tego. Na przykład z tą listą a = ['1', '2', '3', '4', '5'] . - RodriKing


The Samouczek w języku Python mówi o tym (przewiń w dół, aż dojdziesz do części o krojeniu).

Schemat sztuki ASCII jest również przydatny do zapamiętania 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ętywania działania plasterków jest myślenie o wskaźnikach jako wskazujących pomiędzy znaków, z lewą krawędzią pierwszego znaku ponumerowaną 0. Następnie prawą krawędź ostatniego znaku ciągu znaków n znaki mają indeks n.


395
2018-02-03 22:49



W przypadku plasterków z ujemnymi krokami, uważam tę sztukę ASCII za mylącą i powinna ona rozciągać się na -6 od 'ApleH'[:-6:-1] jest poprawnym plasterkiem i różni się od użycia -5 - Chris_Rands
"Jednym ze sposobów zapamiętywania, jak działają plasterki, jest myślenie o wskaźnikach jako wskazujących między postaciami" - to świetny sposób myślenia o tym - jusopi


Wyliczanie możliwości dozwolonych 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, wtedy punkt końcowy będzie nieco niższy niż high-1.

Gdyby stride jest ujemna, kolejność jest nieco zmieniana, 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 cięcie (przecinkami i elipsami) jest najczęściej używane tylko przez specjalne struktury danych (takie jak Numpy); podstawowe sekwencje nie obsługują ich.

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



robi coś dziwnego, jeśli stride jest ujemny jak w [::-1]? - Charlie Parker
@CharlieParker Kiedy strike jest ujemny, od którego się liczy high do low. - ephemient
W rzeczywistości nadal coś zostało pominięte, np. jeśli napiszę "jabłko" [4: -4: -1] dostaję "elp", python tłumaczy -4 na 1 może? - liyuan


Powyższe odpowiedzi nie omawiają przypisania plasterków:

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


199
2018-01-18 21:37



czy możesz wyjaśnić drugą? - chandresh


Wyjaśnij notację Pythona

W skrócie, dwukropki (:) w notacji dolnego indeksu (subscriptable[subscriptarg]) tworzyć notację plastra - która ma opcjonalne argumenty, start, stop, step:

sliceable[start:stop:step]

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

Ważne definicje

Na początek zdefiniujmy kilka terminów:

początek: początkowy indeks plasterka, będzie zawierał element w tym indeksie, chyba że jest taki sam jak zatrzymać, domyślnie przyjmuje wartość 0, tj. pierwszy indeks. Jeśli jest ujemny, oznacza to rozpoczęcie n przedmioty od końca.

zatrzymać: indeks końcowy plasterka, robi to nie zawierają element w tym indeksie, domyślnie długość sekwencji jest podzielona na plasterki, to znaczy do końca włącznie.

krok: ilość, o którą indeks się zwiększa, domyślnie wynosi 1. Jeśli jest ujemna, przekraszacie ją po iteracji w odwrotnej kolejności.

Jak działa indeksowanie

Możesz wykonać dowolną z tych liczb dodatnich lub ujemnych. Znaczenie liczb dodatnich jest proste, ale dla liczb ujemnych, podobnie jak indeksy w Pythonie, liczymy wstecz od końca do początek i zatrzymać, a dla krok, po prostu zmniejszaj swój indeks. Ten przykład jest z samouczka dokumentacji, ale zmieniłem go nieznacznie, aby wskazać, który element w sekwencji, do której odnosi się każdy indeks:

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

Jak działa podziałanie

Aby korzystać z notacji plastra z sekwencją, która go obsługuje, musisz dołączyć co najmniej jeden dwukropek w nawiasach kwadratowych, które podążają za sekwencją (która faktycznie zaimplementować __getitem__ metoda sekwencji, zgodnie z modelem danych Pythona.)

Notacja plasterkowa działa w ten sposób:

sequence[start:stop:step]

I pamiętaj, że są wartości domyślne dla początek, zatrzymać, i krok, więc aby uzyskać dostęp do wartości domyślnych, po prostu pomiń argument.

Notacja plasterkowa, aby uzyskać ostatnie dziewięć elementów z listy (lub jakiejkolwiek innej sekwencji, która je obsługuje, jak ciąg) wyglądałaby tak:

my_list[-9:]

Kiedy to widzę, przeczytałem część w nawiasach jako "9 od końca, do końca". (Właściwie to skracam to mentalnie jako "-9, na")

Wyjaśnienie:

Pełna notacja jest

my_list[-9:None:None]

i do zastąpienia wartości domyślnych (w rzeczywistości kiedy step jest ujemny, stopDomyślnie jest -len(my_list) - 1, więc None na przystanek tak naprawdę oznacza to, że idzie do którejkolwiek końcowej czynności, która go wykonuje):

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

The dwukropek, :, mówi Python, że nadajesz mu kawałek, a nie zwykły indeks. Właśnie dlatego idiomatyczny sposób tworzenia płytkich kopii list w Pythonie 2 jest

list_copy = sequence[:]

A oczyszczanie ich za pomocą:

del my_list[:]

(Python 3 dostaje a list.copy i list.clear metoda.)

Gdy step jest ujemne, domyślne dla start i stop zmiana

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

Ale możesz podać ujemną liczbę całkowitą, a lista (lub większość innych standardowych plików) zostanie podzielona na części od końca do początku.

Zatem ujemny kawałek zmieni domyślne wartości dla start i stop!

Potwierdzam to w źródle

Lubię zachęcać użytkowników do czytania źródła i dokumentacji. The kod źródłowy dla obiektów typu slice i ta logika znajduje się tutaj. Najpierw określamy, czy step jest ujemny:

 step_is_negative = step_sign < 0;

Jeśli tak, dolna granica jest -1  oznacza, że ​​przecinamy całą drogę do początku włącznie, a górna granica to długość minus 1, co oznacza, że ​​zaczynamy na końcu. (Zauważ, że semantyka tego -1 jest różne od -1 użytkownicy mogą przekazywać indeksy w Pythonie, wskazując 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;
}

Inaczej step jest dodatnia, a dolna granica będzie równa zeru, a górna granica (do której dojdziemy, ale nie zostanie uwzględniona) długość pokrojonej listy.

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

Wtedy możemy potrzebować zastosować wartości domyślne dla start i stop - domyślnie dla start jest obliczana jako górna granica kiedy step jest ujemny:

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

i stop, dolna granica:

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

Nadaj swoim plasterkom opisową nazwę!

Może okazać się użyteczne oddzielenie formowania plasterka od przekazania go do list.__getitem__ metoda (to właśnie robią nawiasy kwadratowe). Nawet jeśli nie jesteś dla niego nowy, to sprawia, że ​​Twój kod jest bardziej czytelny, aby inni, którzy mogą czytać Twój kod, mogli łatwiej zrozumieć, co robisz.

Jednak nie można po prostu przypisać do zmiennej liczb całkowitych oddzielonych dwukropkami. Musisz użyć obiektu slice:

last_nine_slice = slice(-9, None)

Drugi argument, None, jest wymagany, aby pierwszy argument był interpretowany jako start argument inaczej byłoby to stop argument.

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

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

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

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

Rozważania dotyczące pamięci:

Ponieważ plasterki list Pythona tworzą nowe obiekty w pamięci, ważną funkcją jest świadomość itertools.islice. Zazwyczaj będziesz chciał wykonać iterację po plasterku, a nie tylko statycznie go utworzyć w pamięci. islice jest do tego idealny. Zastrzeżenie, nie obsługuje negatywnych argumentów start, stop, lub step, więc jeśli jest to problem, może być konieczne obliczenie indeksów lub odwrócenie wartości iteracji 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 na oryginał, a nie kopię.


186
2017-07-12 13:19



Cześć @aaronhall, doskonały post! Czy rozważasz dodanie tego posta do książki Intermediate Python? github.com/yasoob/intermediatePython/issues/153 ? - neowulf33
Chciałem tylko podziękować za ten post, od pewnego czasu piszę Pythona i wciąż do niego wracam, gdy przez cały czas dostaję jednego błędu przez cięcie! - akkatracker
najlepsza odpowiedź tutaj - kskyriacou


I kilka rzeczy, które nie były dla mnie oczywiste, gdy po raz pierwszy zobaczyłem składnię podziału:

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

Prosty sposób na odwrócenie sekwencji!

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

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

124
2018-02-03 23:15





Znalazłem ten świetny stolik 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)

84
2017-09-06 06:50