Κατανόηση της σημειολογίας φέτες

Χρειάζομαι μια καλή εξήγηση (οι αναφορές είναι ένα συν) σχετικά με τη σημειογραφία φέτα της Python's.

Για μένα, αυτός ο συμβολισμός χρειάζεται λίγο μάζεμα.

Φαίνεται εξαιρετικά ισχυρή, αλλά δεν την έχω καταλάβει.

Λύση

Είναι πραγματικά πολύ απλό:

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

Υπάρχει επίσης η τιμή step, η οποία μπορεί να χρησιμοποιηθεί με οποιοδήποτε από τα παραπάνω:

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

Το βασικό σημείο που πρέπει να θυμάστε είναι ότι η τιμή :stop αντιπροσωπεύει την πρώτη τιμή που δεν βρίσκεται στην επιλεγμένη φέτα. Έτσι, η διαφορά μεταξύ stop και start είναι ο αριθμός των στοιχείων που επιλέγονται (αν το step είναι 1, η προεπιλογή).

Το άλλο χαρακτηριστικό είναι ότι το start ή το stop μπορεί να είναι ένας αρνητικός αριθμός, που σημαίνει ότι μετράει από το τέλος του πίνακα αντί για την αρχή. Έτσι:

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

Ομοίως, το step μπορεί να είναι αρνητικός αριθμός:

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 είναι ευγενική με τον προγραμματιστή αν υπάρχουν λιγότερα στοιχεία από αυτά που ζητάτε. Για παράδειγμα, αν ζητήσετε a[:-2] και το a περιέχει μόνο ένα στοιχείο, θα πάρετε μια κενή λίστα αντί για σφάλμα. Μερικές φορές θα προτιμούσατε το σφάλμα, οπότε πρέπει να γνωρίζετε ότι αυτό μπορεί να συμβεί.

Σχέση με το αντικείμενο slice()

Ο τελεστής τεμαχισμού [] στην πραγματικότητα χρησιμοποιείται στον παραπάνω κώδικα με ένα αντικείμενο slice() χρησιμοποιώντας τον συμβολισμό : (ο οποίος είναι έγκυρος μόνο μέσα στο []), δηλ:

a[start:stop:step]

ισοδυναμεί με:

a[slice(start, stop, step)]

Τα αντικείμενα Slice συμπεριφέρονται επίσης ελαφρώς διαφορετικά ανάλογα με τον αριθμό των ορίων, παρόμοια με την range(), δηλαδή υποστηρίζονται τόσο η slice(stop) όσο και η slice(start, stop[, step]). Για να παραλείψετε τον προσδιορισμό ενός συγκεκριμένου ορίσματος, θα μπορούσατε να χρησιμοποιήσετε το None, έτσι ώστε π.χ. το a[start:] να είναι ισοδύναμο με το a[slice(start, None)] ή το a[::-1] να είναι ισοδύναμο με το a[slice(None, None, -1)].

Ενώ η σημειογραφία με βάση το : είναι πολύ χρήσιμη για απλές φέτες, η ρητή χρήση αντικειμένων slice() απλοποιεί την προγραμματιστική παραγωγή φέτες.

Σχόλια (7)

Το σεμινάριο Python μιλάει γι' αυτό (μετακινηθείτε λίγο προς τα κάτω μέχρι να φτάσετε στο σημείο που αναφέρεται στο slicing).

Το διάγραμμα ASCII art είναι επίσης χρήσιμο για να θυμάστε πώς λειτουργούν οι φέτες:

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

Ένας τρόπος για να θυμάστε πώς λειτουργούν οι φέτες είναι να σκεφτείτε ότι οι δείκτες δείχνουν μεταξύ χαρακτήρων, με το αριστερό άκρο του πρώτου χαρακτήρα να έχει τον αριθμό 0. Στη συνέχεια, το δεξί άκρο του τελευταίου χαρακτήρα μιας συμβολοσειράς n χαρακτήρων έχει δείκτη n.

Σχόλια (2)

Απαρίθμηση των δυνατοτήτων που επιτρέπει η γραμματική:

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

Φυσικά, αν (high-low)%stride != 0, τότε το τελικό σημείο θα είναι λίγο πιο χαμηλά από το high-1.

Αν το stride είναι αρνητικό, η σειρά αλλάζει λίγο, αφού μετράμε προς τα κάτω:

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

Οι εκτεταμένες τεμαχισμοί (με κόμματα και ελλείψεις) χρησιμοποιούνται κυρίως μόνο από ειδικές δομές δεδομένων (όπως η NumPy)- οι βασικές ακολουθίες δεν τις υποστηρίζουν.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
Σχόλια (3)