Li

Listas em Python

96 exercícios

Sobre Listas

Uma list é uma coleção mutável de itens em sequência. Como a maioria das coleções (veja os tipos embutidos tuple, dict e set), as listas podem guardar referências a qualquer tipo (ou tipos) de dado, incluindo outras listas. Como qualquer sequência, os itens podem ser acessados por um número de 0-based index a partir da esquerda e -1-based index a partir da direita. As listas podem ser copiadas no todo ou em parte via notação de fatiamento ou <list>.copy().

As listas oferecem tanto operações de sequência comuns quanto mutáveis, como min()/max(), <list>.index(), .append() e .reverse(). Os elementos de uma lista podem ser iterados usando a construção for item in <list>. for index, item in enumerate(<list>) pode ser usado quando tanto o índice quanto o valor do elemento forem necessários.

As listas são implementadas como arrays dinâmicos, parecidas com o tipo Arraylist do Java, e são usadas na maioria das vezes para armazenar grupos de dados semelhantes (strings, números, conjuntos etc.) de tamanho desconhecido (o número de entradas pode aumentar ou diminuir à vontade).

Acessar elementos, verificar se um item pertence à lista com in ou adicionar itens ao lado "direito" de uma lista são operações muito eficientes. Inserir no início (adicionar ao lado "esquerdo") ou inserir no meio de uma lista são bem menos eficientes, porque essas operações exigem deslocar elementos para mantê-los em sequência. Para uma estrutura de dados parecida que oferece appends/pops eficientes em memória dos dois lados, veja collections.deque, que tem aproximadamente o mesmo desempenho O(1) em qualquer direção.

Como as listas são mutáveis e podem conter referências a objetos Python arbitrários, elas também ocupam mais espaço na memória do que um array.array ou uma 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 embutidos do Python produzem listas como resultado.

Construção

Uma list pode ser declarada como um literal com colchetes [] 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, quebras de linha podem ser usadas 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 os adiciona à lista em 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 ao usar um construtor de lista com uma string ou um dict podem surpreender:

# 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, objetos que não são iteráveis vão gerar um TypeError. Por isso, é bem mais fácil criar uma lista com um único item pelo 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]

Acessando elementos

Os itens dentro de listas (assim como os elementos de outros tipos de sequência, como str e tuple), podem ser acessados usando a notação de colchetes. Os índices podem ir da left --> right (começando em zero) ou de right --> left (começando em -1).

índice a partir da esquerda ⟹






0
👇🏾
1
👇🏾
2
👇🏾
3
👇🏾
4
👇🏾
5
👇🏾
P y t h o n
👆🏾
-6
👆🏾
-5
👆🏾
-4
👆🏾
-3
👆🏾
-2
👆🏾
-1





⟸ í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'

Uma seção de uma lista pode ser acessada via notação de fatiamento (<list>[<start>:<stop>]). Uma fatia é definida como uma sequência de elementos na posição index, tal que start <= index < stop. Fatiamento retorna uma cópia dos itens "fatiados" e não modifica a list original.

Um parâmetro step também pode ser usado na fatia (<list>[<start>:<stop>:<step>]) para "pular" ou filtrar os elementos retornados (por exemplo, um step de 2 seleciona um elemento sim, outro não, na seçã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']

Trabalhando com listas

As listas fornecem um iterador e podem ser percorridas em laço da mesma forma que outros tipos de sequência, usando tanto for item in <list> quanto 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 montar uma lista de valores é usar <list>.append() dentro de um laço:

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

Alguns cuidados

Lembre-se de que as variáveis em Python são rótulos que apontam para objetos subjacentes. As lists adicionam mais uma camada como objetos contêiner: elas guardam referências de objeto para os itens que reúnem. Isso pode causar vários problemas ao trabalhar com listas, se não forem tratados corretamente.

Atribuindo mais de um nome de variável

Atribuir um objeto list a um novo nome de variável não copia o objeto list nem seus elementos. Qualquer mudança feita nos elementos da list sob o novo nome afeta o original.

Fazer uma shallow_copy via list.copy() ou fatia evita essa complicação de referência de primeiro nível. Uma shallow_copy cria um novo objeto list, mas não cria novos objetos para os elementos da lista contidos. Esse tipo de cópia geralmente basta para você adicionar ou remover itens dos dois objetos list de forma independente e, na prática, ter 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"]

Essa complicação de referência se agrava ao trabalhar com listas aninhadas ou multiplicadas (os exemplos a seguir vêm do excelente post de blog de 2013 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 nesse caso, uma shallow_copy basta para permitir o comportamento que gostaríamos:

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 mencionado antes, as listas são contêineres de referências, então há uma segunda camada de complicação potencial. Se uma lista contém variáveis, objetos ou estruturas de dados aninhadas, essas referências de segundo nível não serão copiadas via shallow_copy ou fatia. Mutar os objetos subjacentes vai, então, afetar todas as cópias, já 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']]

Tipos de dados relacionados

As listas costumam ser usadas como pilhas e filas, embora sua implementação subjacente torne a inserção no início e no meio lenta. O módulo collections oferece uma variante deque otimizada para appends e pops rápidos em qualquer extremidade, implementada como uma lista duplamente encadeada. Listas aninhadas também são usadas para modelar pequenas matrizes, embora as bibliotecas Numpy e Pandas sejam bem mais robustas para manipulação eficiente de matrizes e dados tabulares. O módulo collections também fornece um tipo UserList que pode ser personalizado para atender a necessidades específicas de listas.

Editar via GitHub O link abre em uma nova janela ou aba

Aprenda Listas

A prática está bloqueada

Desbloqueie mais 5 exercícios para praticar Listas