Μια list είναι μια μεταβλητή συλλογή στοιχείων σε σειρά.
Όπως οι περισσότερες συλλογές (δείτε τους ενσωματωμένους τύπους tuple, dict και set), οι λίστες μπορούν να κρατούν αναφορές σε οποιονδήποτε (ή σε πολλαπλούς) τύπο δεδομένων, ακόμη και σε άλλες λίστες.
Όπως σε κάθε ακολουθία, μπορείς να έχεις πρόσβαση στα στοιχεία μέσω αριθμού 0-based index από τα αριστερά και -1-based index από τα δεξιά.
Οι λίστες μπορούν να αντιγραφούν ολόκληρες ή εν μέρει μέσω συμβολισμού τμήματος ή <list>.copy().
Οι λίστες υποστηρίζουν τόσο κοινές όσο και μεταβλητές λειτουργίες ακολουθίας, όπως min()/max(), <list>.index(), .append() και .reverse().
Μπορείς να διατρέξεις τα στοιχεία μιας λίστας με τη δομή for item in <list>. Το for index, item in enumerate(<list>) μπορεί να χρησιμοποιηθεί όταν χρειάζεσαι και τη θέση και την τιμή του στοιχείου.
Οι λίστες υλοποιούνται ως δυναμικοί πίνακες, παρόμοιοι με τον τύπο Arraylist της Java, και χρησιμοποιούνται συνήθως για την αποθήκευση ομάδων παρόμοιων δεδομένων (συμβολοσειρών, αριθμών, συνόλων κ.λπ.) άγνωστου μήκους (ο αριθμός των καταχωρίσεων μπορεί να αυξηθεί ή να μειωθεί κατά βούληση).
Η πρόσβαση σε στοιχεία, ο έλεγχος αν ένα στοιχείο υπάρχει στη λίστα με το in, ή η προσθήκη στοιχείων στη "δεξιά" πλευρά μιας λίστας είναι όλα πολύ αποδοτικά.
Η προσθήκη στην αρχή (δηλαδή στην "αριστερή" πλευρά) ή η εισαγωγή στη μέση μιας λίστας είναι πολύ λιγότερο αποδοτικές, γιατί αυτές οι πράξεις απαιτούν τη μετατόπιση στοιχείων ώστε να διατηρηθεί η σειρά τους.
Για μια παρόμοια δομή δεδομένων που υποστηρίζει αποδοτικά στη μνήμη appends/pops και από τις δύο πλευρές, δες το collections.deque, το οποίο έχει περίπου την ίδια επίδοση O(1) και προς τις δύο κατευθύνσεις.
Επειδή οι λίστες είναι μεταβλητές και μπορούν να περιέχουν αναφορές σε αυθαίρετα αντικείμενα της Python, καταλαμβάνουν επίσης περισσότερο χώρο στη μνήμη από ένα array.array ή ένα tuple (που είναι αμετάβλητο) ίδιου φαινομενικού μήκους.
Παρ' όλα αυτά, οι λίστες είναι μια εξαιρετικά ευέλικτη και χρήσιμη δομή δεδομένων, και πολλές ενσωματωμένες μέθοδοι και πράξεις της Python παράγουν λίστες ως αποτέλεσμα.
Μια list μπορεί να δηλωθεί ως literal με αγκύλες [] και κόμματα ανάμεσα στα στοιχεία:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
Για λόγους αναγνωσιμότητας, μπορείς να χρησιμοποιήσεις αλλαγές γραμμής όταν υπάρχουν πολλά στοιχεία ή εμφωλευμένες δομές δεδομένων μέσα σε μια λίστα:
>>> lots_of_entries = [
... "Rose",
... "Sunflower",
... "Poppy",
... "Pansy",
... "Tulip",
... "Fuchsia",
... "Cyclamen",
... "Lavender"
... ]
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
... {"fish": "gold", "monkey": "brown", "parrot": "grey"},
... ("fish", "mammal", "bird"),
... ['water', 'jungle', 'sky']
... ]
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
Ο κατασκευαστής list() μπορεί να χρησιμοποιηθεί κενός ή με ένα iterable ως όρισμα.
Ο κατασκευαστής διατρέχει τα στοιχεία του iterable και τα προσθέτει στη λίστα με τη σειρά:
>>> no_elements = list()
>>> no_elements
[]
# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]
# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
>>> multiple_elements_from_set
[2, 3, 5, 7, 11]
Τα αποτελέσματα όταν χρησιμοποιείς τον κατασκευαστή λίστας με μια συμβολοσειρά ή ένα dict μπορεί να εκπλήξουν:
# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")
>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')
>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}
>>> list(source_data)
['fish', 'monkey']
Επειδή ο κατασκευαστής list() δέχεται ως ορίσματα μόνο iterables (ή τίποτα), τα αντικείμενα που δεν είναι iterable θα πετάξουν ένα TypeError. Κατά συνέπεια, είναι πολύ πιο εύκολο να δημιουργήσεις μια λίστα με ένα μόνο στοιχείο μέσω της literal μεθόδου.
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))
>>> one_element_from_iterable
[16]
Στα στοιχεία μέσα σε λίστες (καθώς και στα στοιχεία άλλων τύπων ακολουθίας, όπως str και tuple), μπορείς να έχεις πρόσβαση με τον συμβολισμό αγκυλών.
Οι θέσεις μπορούν να μετρηθούν από τα left --> right (ξεκινώντας από το μηδέν) ή από τα right --> left (ξεκινώντας από το -1).
| θέση από τα αριστερά ⟹ |
|
⟸ θέση από τα δεξιά |
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'
>>> breakfast_foods[-4]
'Oatmeal'
# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'
>>> breakfast_foods[2]
'Eggs'
# Toast is at -1
>>> breakfast_foods[-1]
'Toast'
Σε ένα τμήμα μιας λίστας μπορείς να έχεις πρόσβαση μέσω του συμβολισμού τμήματος (<list>[<start>:<stop>]).
Ένα τμήμα ορίζεται ως μια ακολουθία στοιχείων στη θέση index, τέτοια ώστε start <= index < stop.
Ο τεμαχισμός επιστρέφει ένα αντίγραφο των στοιχείων που "κόπηκαν" και δεν τροποποιεί την αρχική list.
Στο τμήμα μπορεί να χρησιμοποιηθεί και μια παράμετρος step (<list>[<start>:<stop>:<step>]), ώστε να "παρακάμψεις" ή να φιλτράρεις τα στοιχεία που επιστρέφονται (για παράδειγμα, ένα step με τιμή 2 θα επιλέξει ένα στοιχείο παρά ένα στο τμήμα):
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]
>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']
# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]
>>> primary_colors
['Red', 'Yellow', 'Blue']
Οι λίστες παρέχουν έναν iterator και μπορείς να τις διατρέξεις με τον ίδιο τρόπο όπως άλλους τύπους ακολουθίας, χρησιμοποιώντας είτε for item in <list> είτε for index, item in enumerate(<list>):
# Make a list, and then loop through it to print out the elements
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
... print(item)
Orange
Green
Grey
Blue
# Print the same list, but with the indexes of the colors included
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for index, item in enumerate(colors):
... print(item, ":", index)
Orange : 0
Green : 1
Grey : 2
Blue : 3
# Start with a list of numbers and then loop through and print out their cubes.
>>> numbers_to_cube = [5, 13, 12, 16]
>>> for number in numbers_to_cube:
... print(number**3)
125
2197
1728
4096
Ένας συνηθισμένος τρόπος να συνθέσεις μια λίστα τιμών είναι να χρησιμοποιήσεις το <list>.append() μέσα σε έναν βρόχο:
>>> cubes_to_1000 = []
>>> for number in range(11):
... cubes_to_1000.append(number**3)
>>> cubes_to_1000
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
Οι λίστες μπορούν επίσης να συνδυαστούν με διάφορες τεχνικές:
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']
# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3
>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
# Another method for combining 2 lists is to use slice assignment or a loop-append.
# This assigns the second list to index 0 in the first list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
>>> first_one[0:0] = second_one
>>> first_one
['George', 5, 'cat', 'Tabby']
# This loops through the first list and appends its items to the end of the second list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
>>> for item in first_one:
... second_one.append(item)
>>> second_one
['George', 5, 'cat', 'Tabby']
Θυμήσου ότι οι μεταβλητές στην Python είναι ετικέτες που δείχνουν σε υποκείμενα αντικείμενα.
Οι lists προσθέτουν ένα ακόμη επίπεδο ως αντικείμενα-περιέκτες: κρατούν αναφορές σε αντικείμενα για τα στοιχεία που συλλέγουν.
Αυτό μπορεί να οδηγήσει σε πολλά πιθανά προβλήματα όταν δουλεύεις με λίστες, αν δεν το χειριστείς σωστά.
Η ανάθεση ενός αντικειμένου list σε ένα νέο όνομα μεταβλητής δεν αντιγράφει ούτε το αντικείμενο list ούτε τα στοιχεία του.
Οποιαδήποτε αλλαγή κάνεις στα στοιχεία της list κάτω από το νέο όνομα επηρεάζει την αρχική.
Η δημιουργία ενός shallow_copy μέσω list.copy() ή τμήματος θα αποφύγει αυτή την επιπλοκή αναφοράς πρώτου επιπέδου.
Ένα shallow_copy θα δημιουργήσει ένα νέο αντικείμενο list, αλλά δεν θα δημιουργήσει νέα αντικείμενα για τα στοιχεία της λίστας που περιέχονται. Αυτός ο τύπος αντιγραφής συνήθως αρκεί για να προσθέτεις ή να αφαιρείς στοιχεία από τα δύο αντικείμενα list ανεξάρτητα και να έχεις ουσιαστικά δύο "ξεχωριστές" λίστες.
>>> actual_names = ["Tony", "Natasha", "Thor", "Bruce"]
# Assigning a new variable name does not make a copy of the container or its data.
>>> same_list = actual_names
# Altering the list via the new name is the same as altering the list via the old name.
>>> same_list.append("Clarke")
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
>>> actual_names
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
# Likewise, altering the data in the list via the original name will also alter the data under the new name.
>>> actual_names[0] = "Wanda"
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
# If you copy the list, there will be two separate list objects which can be changed independently.
>>> copied_list = actual_names.copy()
>>> copied_list[0] = "Tony"
>>> actual_names
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
>>> copied_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
Αυτή η επιπλοκή με τις αναφορές γίνεται ακόμη πιο έντονη όταν δουλεύεις με εμφωλευμένες ή πολλαπλασιασμένες λίστες (τα παρακάτω παραδείγματα προέρχονται από το εξαιρετικό άρθρο του Ned Batchelder από το 2013, Names and values: making a game board):
from pprint import pprint
# This will produce a game grid that is 8x8, pre-populated with zeros.
>>> game_grid = [[0]*8]*8
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"
# This attempt doesn't work because all the rows are referencing the same underlying list object.
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X']]
Αλλά σε αυτή την περίπτωση, ένα shallow_copy αρκεί για να έχουμε τη συμπεριφορά που θέλουμε:
from pprint import pprint
# This loop will safely produce a game grid that is 8x8, pre-populated with zeros
>>> game_grid = []
>>> filled_row = [0] * 8
>>> for row in range(8):
... game_grid.append(filled_row.copy()) # This is making a new shallow copy of the inner list object each iteration.
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"
# The game grid now works the way we expect it to!
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
Όπως αναφέρθηκε προηγουμένως, οι λίστες είναι περιέκτες αναφορών, οπότε υπάρχει ένα δεύτερο επίπεδο πιθανών επιπλοκών.
Αν μια λίστα περιέχει μεταβλητές, αντικείμενα ή εμφωλευμένες δομές δεδομένων, αυτές οι αναφορές δεύτερου επιπέδου δεν θα αντιγραφούν μέσω shallow_copy ή τμήματος.
Η μεταβολή των υποκείμενων αντικειμένων θα επηρεάσει τότε όλα ανεξαιρέτως τα αντίγραφα, αφού κάθε αντικείμενο list περιέχει μόνο αναφορές που δείχνουν στα στοιχεία που περιέχει.
from pprint import pprint
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
# We'd like a new board, so we make a shallow copy.
>>> new_game_grid = game_grid.copy()
# But a shallow copy doesn't copy the contained references or objects.
>>> new_game_grid[0][0] = 'X'
# So changing the items in the copy also changes the originals items.
>>> pprint(game_grid)
[['X', 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
Οι λίστες χρησιμοποιούνται συχνά ως στοίβες και ουρές, αν και η υποκείμενη υλοποίησή τους κάνει την προσθήκη στην αρχή και την εισαγωγή αργές.
Η ενότητα collections προσφέρει μια παραλλαγή deque, βελτιστοποιημένη για γρήγορες προσθήκες και αφαιρέσεις από οποιοδήποτε άκρο, η οποία υλοποιείται ως διπλά συνδεδεμένη λίστα.
Οι εμφωλευμένες λίστες χρησιμοποιούνται επίσης για να μοντελοποιήσουν μικρούς πίνακες, αν και οι βιβλιοθήκες Numpy και Pandas είναι πολύ πιο ισχυρές για αποδοτικό χειρισμό πινάκων και δεδομένων σε μορφή πίνακα.
Η ενότητα collections παρέχει επίσης έναν τύπο UserList που μπορεί να προσαρμοστεί ώστε να καλύπτει εξειδικευμένες ανάγκες λιστών.