Listas#

Ao longo do capítulo anterior, surgiu uma outra estrutura de dados básica: listas. Vamos entrar mais em detalhes nesta estrutura ao longo deste capítulo.

Definição#

Listas são um dos tipos de dados mais fundamentais e versáteis em Python. Uma lista é uma sequência ordenada de elementos que pode conter uma variedade de tipos de dados, como números, strings, outras listas, objetos, e mais. As listas em Python são mutáveis, o que significa que seus elementos podem ser alterados após a sua criação.

As listas possuem as seguintes caracterísicas:

  • Ordenadas: As listas mantêm a ordem dos elementos conforme são adicionados.

  • Mutáveis: Diferente de alguns outros tipos de coleções que vamos ver na sequência, como tuplas, as listas podem ser modificadas após a sua criação. Você pode adicionar, remover ou alterar seus elementos.

  • Permite duplicatas: Listas podem conter elementos duplicados. Cada elemento na lista mantém sua própria posição e pode ser acessado individualmente.

  • Heterogêneas: Uma lista pode conter elementos de diferentes tipos de dados. Por exemplo, você pode ter uma lista que contém números, strings e até mesmo outras listas.

Sintaxe básica#

O exemplo mais simples é uma lista de inteiros, conforme vimos no capítulo anterior, no terceiro exemplo da seção reforço prático - métodos vs funções.

lista_de_numeros = [10, 50, 40, 65, 90, 70, 30, 156]
print(lista_de_numeros)
[10, 50, 40, 65, 90, 70, 30, 156]

Uma lista é criada com colchetes ([ ]), cada elemento é separado por vírgula (,) e não necessariamente todos os elementos precisam ser do mesmo tipo.

lista_heterogenea = [10, 25.1, "um texto qualquer", True]
print(lista_heterogenea)
[10, 25.1, 'um texto qualquer', True]

Acessando elementos#

Cada elemento tem uma posição (ou índice) na lista começando do índice 0 para o primeiro elemento.

lista_acesso_elementos = [1, "Olá", 3.14, True]

# Acessando elementos da lista
print(lista_acesso_elementos[0])
print(lista_acesso_elementos[1])
1
Olá

Vimos fatiamento de sequencias no capítulo sobre strings, na seção indexação de strings, lembra? É possível usar o fatiamento em listas da mesma forma. Podemos usar exatamente a mesma sintaxe variavel[start:stop:step], com exatamente as mesmas características. Caso queira relembrar o conteúdo, retorne ao capítulo sobre fatiamento de strings.

Aqui vamos abordar apenas o caso que é diferente de strings, no qual temos uma lista de listas.

lista_de_listas = [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]

Vamos começar com uma pergunta:

Quandos elementos tem a lista acima? Pense primeiro e clique para ver a resposta…

Resposta correta: 4 elementos. Execute o código abaixo para conferir.

lista_de_listas = [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
print(len(lista_de_listas))

Porque? Se você contou apenas os números de 1 a 10, talvez você não tenha entendido o conceito de listas. Vamos detalhar mais.

Reparem que a variável acima começa com [[ e termina com ]]. A lista mais externa contém outras listas internas. Listas de listas também são chamadas de listas aninhadas. É como se tivéssemos vários «níveis» de listas, indo por camadas.

lista_de_listas = [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
print(f"Primeiro elemento da lista: {lista_de_listas[0]}")
print(f"Segundo elemento da lista: {lista_de_listas[1]}")
print(f"Terceiro elemento da lista: {lista_de_listas[2]}")
print(f"Quarto elemento da lista: {lista_de_listas[3]}")
Primeiro elemento da lista: [1]
Segundo elemento da lista: [2, 3]
Terceiro elemento da lista: [4, 5, 6]
Quarto elemento da lista: [7, 8, 9, 10]

O caso mais simples que consigo trazer pra representar listas aninhadas é lista = [primeiro_elemento, segundo_elemento, terceiro_elemento, quarto_elemento] onde cada _elemento é também uma lista! Pode parecer confuso, mas creio que este exemplo ajude a esclarecer.

Métodos de listas#

Diferentemente de string, listas tem relativamente poucos métodos, 11 no total, conforme consta na documentação oficial dos métodos de lista. Por este motivo, é possível cobrí-los todos aqui.

Adição de elementos#

Existem 3 métodos para trabalhar com adição de elementos à uma lista:

  • append( ): Adiciona um item ao final da lista.

  • extend( ): Adiciona todos os itens de uma outra sequencia ao final da lista.

  • insert( ): Insere um item em uma posição específica.

Vamos ver em detalhes cada um deles.

append#

O método append(x) adiciona um dado objeto x ao final da lista.

frutas = ["maçã", "banana", "laranja"]
frutas.append("uva")
print(frutas)
['maçã', 'banana', 'laranja', 'uva']

Notem que o objeto como um todo, no caso a string uva, foi adicionada como um objeto único (uma única string) ao final da lista.

extend#

O método extend(x) estende a lista adicionando todos os itens, um a um, de uma outra sequencia. Ele funciona diferente do método append(x) que adiciona o objeto inteiro.

frutas = ["maçã", "banana"]
outras_frutas = ["laranja", "uva"]
frutas.extend(outras_frutas)
print(frutas)
['maçã', 'banana', 'laranja', 'uva']

No exemplo, outras_frutas é uma segunda lista, e os elementos desta listas, laranja e uva, são adicionados à lista frutas. O extend meio que varre uma dada sequencia, e adiciona os elementos um a um na lista.

Vamos entender melhor com mais um exemplo. Se no exemplo do append() nós só trocássemos pelo extend(), veja o que aconteceria:

frutas = ["maçã", "banana", "laranja"]
frutas.extend("uva")
print(frutas)
['maçã', 'banana', 'laranja', 'u', 'v', 'a']

Relembrando que uma string é uma sequencia também, só que de caracteres. Quando passamos a string uva para o extend em frutas.extend("uva"), o que estamos dizendo é: pegue cada elemento (caracter) da string uva e adicione um a um à lista frutas.

É diferente do método append que em frutas.append("uva") nós estamos dizendo: adicione a string inteira uva como um objeto único ao final da lista.

Conseguiram compreender tal diferença?

Como o extend necessariamente varre uma sequência, não é possível estender uma lista a partir de algo que não seja uma sequência, gerando o erro abaixo

lista_de_numeros = [1, 2, 3, 4, 5]
lista_de_numeros.extend(6)
print(lista_de_numeros)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 2
      1 lista_de_numeros = [1, 2, 3, 4, 5]
----> 2 lista_de_numeros.extend(6)
      3 print(lista_de_numeros)

TypeError: 'int' object is not iterable

O erro TypeError: 'int' object is not iterable informa justamente que o tipo int proveniente do 6 passado para o extend não é uma sequência.

Já o append não precisa necessariamente receber uma sequência, pois ele adiciona o objeto em si, e não os elementos de uma dada sequência.

lista_de_numeros = [1, 2, 3, 4, 5]
lista_de_numeros.append(6)
print(lista_de_numeros)
[1, 2, 3, 4, 5, 6]

Para finalizar, um último exemplo mostrando adição de uma lista à outra lista com append e extend.

lista_de_numeros = [1, 2, 3, 4, 5]
lista_de_numeros.append([6, 7, 8, 9])
print(lista_de_numeros)
[1, 2, 3, 4, 5, [6, 7, 8, 9]]
lista_de_numeros = [1, 2, 3, 4, 5]
lista_de_numeros.extend([6, 7, 8, 9])
print(lista_de_numeros)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Conforme exemplificados acima, o append adiciona a lista [6, 7, 8, 9] como um objeto único, um elemento a mais na lista inicial, obtendo como resultado uma lista aninhada. O tamanho da lista final é o tamanho da lista inicial + 1 elemento.

No caso do extend, os elementos da lista [6, 7, 8, 9] são adicionados um a um à lista inicial. O tamanho da lista final é é o total de elementos da lista inicial + o total de elementos da lista estendida.

insert#

Tanto o append quanto o extend só adicionam/estendem elementos ao final da lista. O insert tem o mesmo comportamento do append, sendo possível especificar o índice na qual o elemento será inserido.

Nesta altura do livro, espero que você tenha entendido e se lembre que a indexação em Python começa por 0!

frutas = ["maçã", "banana", "laranja"]
frutas.insert(1, "morango")
print(frutas)
['maçã', 'morango', 'banana', 'laranja']

Remoção de elementos#

Para remoção de elementos, também temos 3 métodos:

  • remove( ): Remove a primeira ocorrência de um item da lista.

  • pop( ): Remove e retorna o item em uma posição específica.

  • clear( ): Remove todos os itens da lista.

remove#

O método remove apaga a primeira ocorrência da esquerda para direita de um item da lista.

frutas = ["maçã", "banana", "laranja", "banana"]
frutas.remove("banana")
print(frutas)
['maçã', 'laranja', 'banana']

Observem que tínhamos 2 ocorrências de banana na lista inicial, nos índices 1 e 3 da lista. A primeira aplicação de frutas.remove('banana') removeu apenas banana do índice 1.

frutas = ["maçã", "banana", "laranja", "banana"]
frutas.remove("fruta inexistente")
print(frutas)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[15], line 2
      1 frutas = ["maçã", "banana", "laranja", "banana"]
----> 2 frutas.remove("fruta inexistente")
      3 print(frutas)

ValueError: list.remove(x): x not in list

Caso o elemento não seja encontrado, o nosso código vai apresentar o erro com a mensagem ValueError: list.remove(x): x not in list, informando que o elemento passado não está na lista

pop#

O método pop remove e retorna o item em um índice específico.

O pop atua diferente do remove. No remove passamos o elemento em si, e no pop passamos a posição do elemento que será removido, e não o elemento em si.

frutas = ["maçã", "banana", "laranja"]
frutas.pop(0)
print(frutas)
['banana', 'laranja']

Além disso, há outra diferença. No caso do remove, não conseguimos salvar o elemento removido em uma variável, pois ele não é retornado. Já no pop, o elemento removido é retornado, sendo possível salvá-lo em uma variável para uso posterior. Vejamos exemplos:

Quando salvamos o resultado de frutas.remove("banana") na variável fruta_removida, ela não recebe nenhum valor, e acaba representanda pelo valor nulo None em Python.

frutas = ["maçã", "banana", "laranja", "banana"]
fruta_removida = frutas.remove("banana")
print(fruta_removida)
None

Porém, quando salvamos o resultado de frutas.pop(0) na variável fruta_removida, ela recebe o valor do elemento removido. Por isso frisei a funcionalidade do pop como remove e retorna o item pela posição.

frutas = ["maçã", "banana", "laranja", "banana"]
fruta_removida = frutas.pop(0)
print(fruta_removida)
print(frutas)
maçã
['banana', 'laranja', 'banana']

Quando nenhum índice é passado para o pop, ele remove e retorna o último elemento da lista por padrão.

frutas = ["maçã", "banana", "laranja", "banana"]
fruta_removida = frutas.pop()
print(fruta_removida)
print(frutas)
banana
['maçã', 'banana', 'laranja']

clear#

O método clear é simples. Ele limpa toda a lista, removendo todos os elementos. Aqui não há segredos.

frutas = ["maçã", "banana", "laranja", "banana"]
frutas.clear()
print(frutas)
[]

Informações sobre a lista#

Aqui temos 2 métodos:

  • index(): Retorna o índice da primeira ocorrência de um item.

  • count(): Retorna o número de ocorrências de um item na lista.

index#

O método index é bem simples e serve para buscar pelo menor índice de um dado elemento, sendo muito útil em localizar a posição de itens em uma lista. No exemplo abaixo, o primeiro índice encontrado para banana é o índice 1.

frutas = ["maçã", "banana", "laranja", "banana"]
indice = frutas.index("banana")
print("Menor índice encontrado:", indice)
Menor índice encontrado: 1

Caso o elemento não seja encontrado, o erro ValueError equivalente ao remove aparecerá.

frutas = ["maçã", "banana", "laranja", "banana"]
indice = frutas.index("fruta inexistente")
print("Menor índice encontrado:", indice)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[22], line 2
      1 frutas = ["maçã", "banana", "laranja", "banana"]
----> 2 indice = frutas.index("fruta inexistente")
      3 print("Menor índice encontrado:", indice)

ValueError: 'fruta inexistente' is not in list

count#

O método count é similar ao que vimos em strings, para contar quantas vezes um dado elemento aparece na lista.

frutas = ["maçã", "banana", "laranja", "banana"]
contador = frutas.count("banana")
print("Quantidade de ocorrências de banana encontrada:", contador)
Quantidade de ocorrências de banana encontrada: 2

Ordenação e reversão#

Temos 2 métodos aqui:

  • sort(): Ordena os itens da lista.

  • reverse(): Inverte a ordem dos itens na lista.

sort#

O método sort ordena uma dada lista em ordem crescente ou decrescente. Para definir entre crescente e decrescente é possível usar o parâmetro reverse, que, por padrão, assume o valor reverse=False. Em outras palavras, o padrão de ordenação, quando nenhum parâmetro é passado, é crescente.

numeros = [3, 1, 4, 1, 5, 9, 2]
numeros.sort()
print("Ordem crescente:", numeros)

numeros.sort(reverse=True)
print("Ordem decrescente:", numeros)
Ordem crescente: [1, 1, 2, 3, 4, 5, 9]
Ordem decrescente: [9, 5, 4, 3, 2, 1, 1]

Quando os elementos são strings, a ordenação é feita de forma alfabética.

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
nomes.sort()
print("Ordem alfabética:", nomes)

nomes.sort(reverse=True)
print("Ordem alfabética reversa:", nomes)
Ordem alfabética: ['Antônio', 'Caio', 'Joaquim', 'Jonas', 'José', 'João']
Ordem alfabética reversa: ['João', 'José', 'Jonas', 'Joaquim', 'Caio', 'Antônio']

Quando os tipos de dados da lista são misturados, não é possível fazer a ordenação.

tipos_misturados = [1, "1", "Henrique", 4.0]
tipos_misturados.sort()
print("Ordem crescente:", tipos_misturados)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[26], line 2
      1 tipos_misturados = [1, "1", "Henrique", 4.0]
----> 2 tipos_misturados.sort()
      3 print("Ordem crescente:", tipos_misturados)

TypeError: '<' not supported between instances of 'str' and 'int'

reverse#

O método reverse inverte uma dada lista ao contrário. É diferente do sort, que ordena.

tipos_misturados = [1, "1", "Henrique", 4.0]
tipos_misturados.reverse()
print("Lista invertida:", tipos_misturados)
Lista invertida: [4.0, 'Henrique', '1', 1]

Outras operações com listas#

Pertencimento#

É possível verificar se um dado elemento está ou não em uma lista usando os operadores not e in

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
print("João" in nomes)
True
nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
print("Nome inexistente" not in nomes)
True

Comprimento#

Como já vimos em strings, é possível usar a função len para mensurar o tamanho de uma lista.

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
quantidade_nomes = len(nomes)
print(f"A lista tem {quantidade_nomes} nomes.")
A lista tem 6 nomes.

Lembrete (len é função e não método)

A esta altura do livro você deve estar confortável em saber porque usamos len(nomes) e não nomes.len(). O motivo: `len()´ é uma função e não um método de lista, conforme vimos na seção reforço prático - métodos vs funções.

Iterando pela lista#

Podemos já adiantar um tema que vamos detalhar mais em capítulos futuros, o laço for. Basicamente, e de forma simplificada, podemos iterar pelos elementos de uma lista.

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
for nome in nomes:
    print(nome)
João
José
Jonas
Joaquim
Caio
Antônio

No exemplo acima, estamos usando uma variável temporária nome, que recebe cada um dos nomes da lista nomes. Cada item da lista é impresso na tela.

Nota (identação)

Reparem bem na linha     print(nome). Há vários espaços em branco à direita dela. Pela primeira vez estamos usando identação no código.

Em Python, a identação são espaços que colocamos no começo das linhas. Ela ajuda o computador a entender que partes do código pertencem juntas.

A linha print(nome) está indentada, o que significa que ela pertence ao bloco do laço de repetição for da linha acima. O que está indentado aqui é a ação que queremos realizar para cada nome na lista. Nesse caso, a ação é imprimir o nome na tela.

Vamos detalhar mais a fundo a identação quando começarmos a estudar estruturas de decisão if-else e laços de repetição for e while.

O que você precisa entender por agora é que a identação é obrigatória, caso contrário o código vai gerar um erro. Experimente executar o trecho abaixo e veja você mesmo o erro:

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
for nome in nomes:
print(nome)

Substituindo valores#

Como vimos bem lá no começo deste capítulo, listas são objetos mutáveis. Por isso temos vários métodos de inserção e deleção de itens em uma lista. Mas podemos também alterar valores já existentes. Vejamos um exemplo:

nomes = ["João", "José", "Jonas", "Joaquim", "Caio", "Antônio"]
nomes[0] = "Fernando"
print(nomes)
['Fernando', 'José', 'Jonas', 'Joaquim', 'Caio', 'Antônio']

Aqui, a linha nomes[0] = "Fernando" é responsável por substituir no índice 0 o nome existente João pelo Fernando.