A list egy sorrendben tárolt elemekből álló, módosítható gyűjtemény.
A legtöbb gyűjteményhez hasonlóan (_lásd a beépített tuple, dict és set típusokat) a listák bármilyen (vagy többféle) adattípusra tárolhatnak hivatkozást, beleértve a többi listát is.
Ahogy bármely sorozat esetében, az elemek balról 0-based index számmal, jobbról pedig -1-based index számmal érhetők el.
A listák egészben vagy részben másolhatók a szeletelési jelöléssel vagy a <list>.copy() metódussal.
A listák támogatják mind az általános, mind a módosítható sorozatműveleteket, például a min()/max(), <list>.index(), .append() és .reverse() metódust.
A listák elemein a for item in <list> szerkezettel lehet végigiterálni. Ha az elem indexére és értékére is szükség van, akkor a for index, item in enumerate(<list>) használható.
A listák dinamikus tömbökként vannak megvalósítva, hasonlóan a Java Arraylist típusához, és leggyakrabban hasonló adatok (stringek, számok, halmazok stb.) ismeretlen hosszúságú csoportjainak tárolására használják őket (a bejegyzések száma tetszőlegesen növekedhet vagy csökkenhet).
Az elemek elérése, a tagság ellenőrzése az in operátorral, vagy az elemek hozzáfűzése a lista „jobb oldalához” mind nagyon hatékony.
Az elékapcsolás (hozzáfűzés a „bal oldalhoz”) vagy a lista közepébe való beszúrás sokkal kevésbé hatékony, mert ezek a műveletek az elemek eltolását igénylik ahhoz, hogy megőrizzék a sorrendjüket.
Ha olyan hasonló adatszerkezetet keresel, amely mindkét oldalról memóriahatékony appends/pops műveletet tesz lehetővé, lásd a collections.deque típust, amely mindkét irányban megközelítőleg ugyanolyan O(1) teljesítményt nyújt.
Mivel a listák módosíthatók, és tetszőleges Python-objektumokra tartalmazhatnak hivatkozásokat, több helyet foglalnak a memóriában, mint egy ugyanolyan látszólagos hosszúságú array.array vagy tuple (amely módosíthatatlan).
Ennek ellenére a listák rendkívül rugalmas és hasznos adatszerkezet, és a Python számos beépített metódusa és művelete listát ad eredményül.
A list literálként is létrehozható szögletes [] zárójelekkel és az elemek közötti vesszőkkel:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
Az olvashatóság érdekében sortörések is használhatók, ha egy listában sok elem vagy egymásba ágyazott adatszerkezet található:
>>> 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']]
A list() konstruktor üresen vagy egy iterálható argumentummal is használható.
Az iterálható elemein a konstruktor végigmegy, és sorrendben hozzáadja őket a listához:
>>> 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]
A lista konstruktornak stringgel vagy dict-tel való használatakor az eredmény meglepő lehet:
# 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']
Mivel a list() konstruktor argumentumként csak iterálhatókat (vagy semmit) fogad el, a nem iterálható objektumok TypeError hibát dobnak. Következésképpen sokkal egyszerűbb egyetlen elemből álló listát a literál módszerrel létrehozni.
# 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]
A listán belüli elemek (valamint más sorozattípusok, például a str és tuple elemei) zárójeles jelöléssel érhetők el.
Az indexek left --> right (nullától indulva) vagy right --> left (-1-től indulva) irányban haladhatnak.
| index balról ⟹ |
|
⟸ index jobbról |
>>> 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'
A lista egy szakasza szeletelési jelöléssel (<list>[<start>:<stop>]) érhető el.
A szelet az index pozícióban lévő elemsorozat, ahol start <= index < stop.
A szeletelés a kivágott elemek másolatát adja vissza, és nem módosítja az eredeti list objektumot.
A szeletben egy step paraméter is használható (<list>[<start>:<stop>:<step>]), hogy „kihagyd” vagy szűrd a visszaadott elemeket (például egy step értéke 2 esetén a szakasz minden második elemét választja ki):
>>> 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']
A listák iterátort biztosítanak, és ugyanúgy végig lehet rajtuk iterálni, mint más sorozattípusokon, akár for item in <list>, akár for index, item in enumerate(<list>) formában:
# 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
Az egyik gyakori módja egy értéklista összeállításának, hogy egy cikluson belül <list>.append() metódust használunk:
>>> 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]
A listák különféle technikákkal is összevonhatók:
# 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']
Emlékezz rá, hogy a Pythonban a változók címkék, amelyek mögöttes objektumokra mutatnak.
A lists egy újabb réteget ad hozzá tárolóobjektumokként: az összegyűjtött elemeikhez objektumokra vonatkozó hivatkozásokat tárolnak.
Ez több lehetséges problémához vezethet a listákkal való munka során, ha nem kezeljük megfelelően.
Ha egy list objektumot egy új változó_névhez_ rendelsz, az nem másolja le a list objektumot és annak elemeit.
A listában az új néven keresztül végrehajtott bármilyen változtatás az eredetire is hatással van.
A shallow_copy készítése a list.copy() vagy a szeletelés segítségével elkerüli ezt az első szintű hivatkozási bonyodalmat.
A shallow_copy egy új list objektumot hoz létre, de nem hoz létre új objektumokat a benne lévő lista_elemek_ számára. Ez a fajta másolás általában elegendő ahhoz, hogy a két list objektumból egymástól függetlenül adj hozzá vagy távolíts el elemeket, és gyakorlatilag két „különálló” listád legyen.
>>> 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"]
Ez a hivatkozási bonyodalom tovább súlyosbodik az egymásba ágyazott vagy sokszorozott listák esetében (a következő példák a kiváló, 2013-as Ned Batchelder blogbejegyzésből származnak: 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']]
Ebben az esetben viszont egy shallow_copy is elég ahhoz, hogy megkapjuk a kívánt viselkedést:
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']]
Ahogy korábban említettük, a listák hivatkozások tárolói, így van egy második lehetséges bonyodalmi réteg is.
Ha egy lista változókat, objektumokat vagy egymásba ágyazott adatszerkezeteket tartalmaz, akkor ezek a másodszintű hivatkozások nem lesznek lemásolva a shallow_copy vagy a szeletelés során.
A mögöttes objektumok módosítása ezután az összes példányra hatással lesz, mivel minden list objektum csak a benne lévő elemekre mutató hivatkozásokat tartalmaz.
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']]
A listákat gyakran használják veremként és sorként, bár a mögöttes megvalósításuk miatt az elékapcsolás és a beszúrás lassú.
A collections modul kínál egy deque változatot, amely mindkét végéről gyors hozzáfűzésre és kivételre van optimalizálva, és kétszeresen láncolt listaként van megvalósítva.
Az egymásba ágyazott listákat kis mátrixok modellezésére is használják, bár a Numpy és Pandas könyvtárak sokkal robusztusabbak a hatékony mátrix- és táblázatos adatkezeléshez.
A collections modul egy UserList típust is biztosít, amely testreszabható a speciális listás igényekhez.