Uma list é uma coleção mutável de itens em sequência.
Como a maioria das coleções (vê os tipos incorporados tuple, dict e set), as listas podem guardar referências a qualquer tipo de dados (ou a vários), incluindo outras listas.
Tal como em qualquer sequência, podes aceder aos itens através de um número 0-based index a partir da esquerda e de um -1-based index a partir da direita.
As listas podem ser copiadas total ou parcialmente através da notação de fatiamento ou de <list>.copy().
As listas suportam operações de sequência tanto comuns como mutáveis, tais como min()/max(), <list>.index(), .append() e .reverse().
Podes iterar sobre os elementos de uma lista com a construção for item in <list>. A construção for index, item in enumerate(<list>) pode ser usada quando precisas tanto do índice como do valor do elemento.
As listas são implementadas como arrays dinâmicos, semelhantes ao tipo Arraylist do Java, e são usadas sobretudo para guardar grupos de dados semelhantes (strings, números, conjuntos, etc.) de comprimento desconhecido (o número de entradas pode aumentar ou diminuir arbitrariamente).
Aceder a elementos, verificar a pertença com in ou acrescentar itens ao lado «direito» de uma lista são operações muito eficientes.
Acrescentar ao início (acrescentar ao lado «esquerdo») ou inserir no meio de uma lista é muito menos eficiente, porque essas operações exigem deslocar elementos para os manter em sequência.
Para uma estrutura de dados semelhante que suporta appends/pops eficientes em termos de memória a partir de ambos os lados, vê collections.deque, que tem aproximadamente o mesmo desempenho O(1) em qualquer das direções.
Como as listas são mutáveis e podem conter referências a objetos Python arbitrários, também ocupam mais espaço na memória do que um array.array ou um tuple (que é imutável) do mesmo comprimento aparente.
Apesar disso, as listas são uma estrutura de dados extremamente flexível e útil, e muitos métodos e operações incorporados do Python devolvem listas como resultado.
Uma list pode ser declarada como um literal com parênteses retos [] e vírgulas entre os elementos:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
Para facilitar a leitura, podes usar quebras de linha quando há muitos elementos ou estruturas de dados aninhadas dentro de uma lista:
>>> 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']]
O construtor list() pode ser usado vazio ou com um iterável como argumento.
O construtor percorre os elementos do iterável e acrescenta-os à lista por ordem:
>>> 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]
Os resultados obtidos ao usar um construtor de lista com uma string ou um dict podem ser surpreendentes:
# 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']
Como o construtor list() só aceita iteráveis (ou nada) como argumentos, os objetos que não são iteráveis geram um TypeError. Por isso, é muito mais fácil criar uma lista com um único item através do método 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]
Podes aceder aos itens dentro das listas (bem como aos elementos de outros tipos de sequência, como str e tuple) através da notação de parênteses retos.
Os índices podem ir da left --> right (a começar em zero) ou da right --> left (a começar em -1).
| índice a partir da esquerda ⟹ |
|
⟸ índice a partir da direita |
>>> 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'
Podes aceder a uma secção de uma lista através da notação de fatiamento (<list>[<start>:<stop>]).
Uma fatia define-se como uma sequência de elementos na posição index, tal que start <= index < stop.
O fatiamento devolve uma cópia dos itens «fatiados» e não modifica a list original.
Também podes usar um parâmetro step na fatia (<list>[<start>:<stop>:<step>]) para «saltar» ou filtrar os elementos devolvidos (por exemplo, um step de 2 seleciona um elemento sim, outro não, dentro da secção):
>>> 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']
As listas fornecem um iterador e podem ser percorridas da mesma forma que outros tipos de sequência, usando for item in <list> ou 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
Uma forma comum de compor uma lista de valores é usar <list>.append() dentro de um ciclo:
>>> 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]
As listas também podem ser combinadas através de várias técnicas:
# 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']
Lembra-te de que as variáveis em Python são etiquetas que apontam para objetos subjacentes.
As lists acrescentam mais uma camada enquanto objetos contentores: guardam referências de objetos para os itens que reúnem.
Isto pode dar origem a vários problemas potenciais quando trabalhas com listas, se não forem tratados devidamente.
Atribuir um objeto list a um novo nome de variável não copia o objeto list nem os seus elementos.
Qualquer alteração feita aos elementos da list com o novo nome afeta a original.
Fazer uma shallow_copy com list.copy() ou uma fatia evita esta complicação de referências de primeiro nível.
Uma shallow_copy cria um novo objeto list, mas não cria novos objetos para os elementos de lista contidos. Este tipo de cópia costuma ser suficiente para adicionares ou removeres itens dos dois objetos list de forma independente e teres, na prática, duas listas «separadas».
>>> 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"]
Esta complicação de referências agrava-se quando trabalhas com listas aninhadas ou multiplicadas (os exemplos seguintes são do excelente artigo de blog de 2013 de Ned Batchelder, 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']]
Mas nesta circunstância, uma shallow_copy é suficiente para permitir o comportamento que pretendemos:
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']]
Como referimos antes, as listas são contentores de referências, pelo que há uma segunda camada de complicação potencial.
Se uma lista contiver variáveis, objetos ou estruturas de dados aninhadas, essas referências de segundo nível não serão copiadas com shallow_copy nem com uma fatia.
Alterar os objetos subjacentes afetará então todas e quaisquer cópias, dado que cada objeto list contém apenas referências que apontam para os elementos contidos.
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']]
As listas são frequentemente usadas como pilhas e filas, embora a sua implementação subjacente torne lentas as operações de acrescentar ao início e de inserir.
O módulo collections oferece uma variante deque, otimizada para acrescentar e retirar rapidamente em qualquer das extremidades, implementada como uma lista duplamente ligada.
As listas aninhadas também são usadas para modelar pequenas matrizes, embora as bibliotecas Numpy e Pandas sejam muito mais robustas para manipular matrizes e dados tabulares de forma eficiente.
O módulo collections também disponibiliza um tipo UserList que podes personalizar para satisfazer necessidades específicas de listas.