Vraag Inzicht in de slice-notatie van Python


Ik heb een goede uitleg nodig (referenties zijn een plus) voor de slice-notatie van Python.

Voor mij heeft deze notatie een beetje van het oppakken nodig.

Het ziet er buitengewoon krachtig uit, maar ik ben er niet helemaal uit.


2290
2018-02-03 22:31


oorsprong


antwoorden:


Het is eigenlijk vrij eenvoudig:

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

Er is ook de step waarde, die kan worden gebruikt met een van de bovenstaande:

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

Het belangrijkste om te onthouden is dat het :end waarde vertegenwoordigt de eerste waarde die dat is niet in het geselecteerde segment. Dus het verschil tussen end en start is het aantal geselecteerde elementen (indien step is 1, de standaardinstelling).

Het andere kenmerk is dat start of end misschien een negatief getal, wat betekent dat het telt vanaf het einde van de array in plaats van vanaf het begin. Zo:

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

Evenzo step kan een negatief getal zijn:

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 is vriendelijk voor de programmeur als er minder items zijn dan waar je om vraagt. Bijvoorbeeld als u erom vraagt a[:-2] en a bevat slechts één element, u krijgt een lege lijst in plaats van een fout. Soms zou je de fout liever hebben, dus je moet je ervan bewust zijn dat dit kan gebeuren.


3094
2018-02-03 22:48



De Python-zelfstudie praat erover (scroll een beetje naar beneden tot je bij het gedeelte over slicen komt).

Het ASCII-diagram is ook handig om te onthouden hoe segmenten werken:

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

Een manier om te onthouden hoe segmenten werken, is door de indices te beschouwen als wijzend tussen tekens, met de linkerrand van het eerste teken met het nummer 0. Dan de rechterrand van het laatste teken van een reeks van n tekens heeft een index n.


394
2018-02-03 22:49



Opsommen van de mogelijkheden die de grammatica toelaat:

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

Natuurlijk als (high-low)%stride != 0, dan zal het eindpunt een beetje lager zijn dan high-1.

Als stride is negatief, de volgorde is een beetje veranderd sinds we aftellen:

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

Uitgebreide slicing (met komma's en ellipsen) worden meestal alleen gebruikt door speciale datastructuren (zoals Numpy); de basissequenties ondersteunen ze niet.

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



De bovenstaande antwoorden bespreken de slice-toewijzing niet:

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

Dit kan ook het verschil tussen slicing en indexering verduidelijken.


199
2018-01-18 21:37



Leg de slice-notatie van Python uit

Kortom, de dubbele punten (:) in subscript notatie (subscriptable[subscriptarg]) maak plaknotatie - die de facultatieve argumenten heeft, start, stop, step:

sliceable[start:stop:step]

Python-slicing is een computationeel snelle manier om delen van uw gegevens methodisch te benaderen. Naar mijn mening, om zelfs maar een tussenliggende Python-programmeur te zijn, is het een aspect van de taal waarmee je bekend moet zijn.

Belangrijke definities

Laten we om te beginnen een aantal termen definiëren:

begin: de beginindex van het segment, het bevat het element bij deze index tenzij het hetzelfde is als hou op, staat standaard op 0, d.w.z. de eerste index. Als het negatief is, betekent dit om te beginnen n items van het einde.

hou op: de eindindex van het segment, dat doet het niet neem het element op deze index op, standaard de lengte van de reeks die wordt gesegmenteerd, dat wil zeggen, tot en met het einde.

stap: de hoeveelheid waarmee de index stijgt, staat standaard op 1. Als het negatief is, snijd je het iterabel in omgekeerde richting.

Hoe indexering werkt

Je kunt elk van deze positieve of negatieve getallen maken. De betekenis van de positieve getallen is eenvoudig, maar voor negatieve getallen, net als indices in Python, telt u vanaf het einde terug voor de begin en hou op, en voor de stap, u verlaag eenvoudigweg uw index. Dit voorbeeld is uit de handleiding van de documentatie, maar ik heb het enigszins aangepast om aan te geven welk item in een reeks elke index verwijst:

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

Hoe slicing werkt

Als u segmentnotatie wilt gebruiken met een reeks die dit ondersteunt, moet u ten minste één dubbele punt opnemen tussen de vierkante haken die de reeks volgen (die eigenlijk implementeer de __getitem__ methode van de reeks, volgens het Python-gegevensmodel.)

Slice-notatie werkt als volgt:

sequence[start:stop:step]

En herinner eraan dat er standaardwaarden zijn voor begin, hou op, en stap, dus om de standaardinstellingen te gebruiken, laat u eenvoudigweg het argument achterwege.

Segmentnotatie om de laatste negen elementen uit een lijst te krijgen (of een andere reeks die dit ondersteunt, zoals een tekenreeks) ziet er als volgt uit:

my_list[-9:]

Als ik dit zie, lees ik het gedeelte tussen de haakjes als "9e van het einde, tot het einde". (Eigenlijk kort ik het mentaal af als "-9, aan")

Uitleg:

De volledige notatie is

my_list[-9:None:None]

en om de standaardwaarden te vervangen (eigenlijk wanneer step is negatief, stopstandaard is -len(my_list) - 1, dus None voor stop betekent eigenlijk alleen dat het naar de eindstap gaat waar het naartoe gaat):

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

De dikke darm, :, is wat Python vertelt dat je het een segment geeft en geen reguliere index. Dat is de reden waarom de idiomatische manier om een ​​ondiepe kopie van lijsten te maken in Python 2 is

list_copy = sequence[:]

En ze opruimen is met:

del my_list[:]

(Python 3 krijgt een list.copy en list.clear methode.)

Wanneer step is negatief, de standaardinstellingen voor start en stop verandering

Standaard, wanneer de step argument is leeg (of None), het is toegewezen aan +1.

U kunt echter een negatief geheel getal doorgeven en de lijst (of de meeste andere standaard slicables) wordt van het einde tot het begin in plakken gedeeld.

Dus een negatief segment zal de standaardinstellingen veranderen start en stop!

Bevestigen in de bron

Ik moedig gebruikers aan om zowel de bron als de documentatie te lezen. De broncode voor segmentobjecten en deze logica is hier te vinden. Eerst bepalen we of step is negatief:

 step_is_negative = step_sign < 0;

Als dat zo is, is de ondergrens -1  wat betekent dat we helemaal doorgaan tot en met het begin, en dat de bovengrens de lengte minus 1 is, wat betekent dat we aan het einde beginnen. (Merk op dat de semantiek hiervan -1 is verschillend van een -1 dat gebruikers indexen in Python kunnen doorgeven met vermelding van het laatste item.)

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

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

Anders step is positief en de ondergrens is nul en de bovengrens (waarmee we naar boven gaan maar niet inclusief) de lengte van de gesplitste lijst.

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

Vervolgens moeten we mogelijk de standaardinstellingen toepassen start en stop - de standaardwaarde voor start wordt berekend als de bovengrens wanneer step is negatief:

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

en stop, de ondergrens:

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

Geef je plakjes een beschrijvende naam!

Misschien vindt u het nuttig om het vormen van het segment te scheiden van het doorgeven ervan aan het list.__getitem__ methode (dat is wat de vierkante haakjes doen). Zelfs als u niet nieuw bent, blijft uw code leesbaar, zodat anderen die uw code mogelijk moeten lezen, sneller begrijpen wat u doet.

U kunt echter niet enkele gehele getallen gescheiden door dubbele punten aan een variabele toewijzen. U moet het slice-object gebruiken:

last_nine_slice = slice(-9, None)

Het tweede argument, None, is vereist, zodat het eerste argument wordt geïnterpreteerd als het start argument anders zou het de stop argument.

U kunt het segmentobject vervolgens doorgeven aan uw reeks:

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

Het is interessant dat reeksen ook segmenten nemen:

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

Geheugen overwegingen:

Omdat segmenten van Python-lijsten nieuwe objecten in het geheugen creëren, is een andere belangrijke functie die je moet kennen, wel itertools.islice. Meestal wilt u een segment herhalen, niet alleen statisch in het geheugen laten maken. islice is hier perfect voor. Een voorbehoud, het ondersteunt geen negatieve argumenten voor start, stopof step, dus als dat een probleem is, moet je misschien indices berekenen of het iterabele van tevoren ongedaan maken.

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

en nu:

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

Het feit dat lijstsegmenten een kopie maken, is een functie van lijsten zelf. Als u geavanceerde objecten als een Pandas DataFrame snijdt, kan dit een weergave van het origineel en niet van een kopie opleveren.


184
2017-07-12 13:19



En een paar dingen die me niet meteen duidelijk waren toen ik de slicingssyntaxis voor het eerst zag:

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

Gemakkelijke manier om sequenties om te keren!

En als je om de een of andere reden elk tweede item in de omgekeerde volgorde wilde:

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

124
2018-02-03 23:15



Ik heb deze geweldige tafel gevonden 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