Manipulação de listas em Python


Introdução

Saber manipular listas em Python é de extrema importância, pois listas são um dos principais tipos de dados em Python.

Nós iremos cobrir bastante conteúdo nesta seção, então leia com calma, sabendo que dominar o funcionamento de listas em Python aumentará muito sua produtividade ao programar.

Em Python, listas de objetos são representadas pelo tipo list. Esse tipo de dados é basicamente uma sequência de elementos, que podem ou não ser do mesmo tipo.

Vejamos alguns exemplos básicos de operações envolvendo listas.

# Cria uma lista sem nenhum elemento.
# A expressão lista_vazia = list() possui o mesmo efeito.
lista_vazia = []
print("Lista vazia: ", lista_vazia)
print("Tipo de uma lista: ", type(lista_vazia))

lista_inteiros = [10, 20, 30, 40]
print("Lista de inteiros: ", lista_inteiros)

lista_tipos_diferentes = ["George", "Orwell", 1984]
print("Lista de elementos com tipos diferentes: ", lista_tipos_diferentes)
Lista vazia:  []
Tipo de uma lista:  <class 'list'>
Lista de inteiros:  [10, 20, 30, 40]
Lista de elementos com tipos diferentes:  ['George', 'Orwell', 1984]

Python permite também a criação de listas aninhadas (uma lista dentro da outra). Este recurso é útil quando desejamos criar listas de várias dimensões (ou matrizes):

lista_aninhada_simples = [a, [b, c, d]]
print("Lista aninhada simples: ", lista_aninhada)

# Lista aninhada com vários níveis
lista_aninhada = [1, [2, [3, [4]]], 5]
print("Outra lista aninhada: ", lista_aninhada)

matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print("Matriz 3x3: ", matriz)
Lista aninhada simples:  [a, [b, c, d]]
Outra lista aninhada:  [1, [2, [3, [4]]], 5]
Matriz 3x3:  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Percorrendo Listas

A forma mais simples de se percorrer todos os elementos de uma lista em Python é usando o operador in, como mostrado abaixo:

nums_impares = [1, 3, 5, 7, 9, 11]
for num in nums_impares:
    print(num)
1
3
5
7
9
11
# Percorrendo uma lista de palavras.
print("Palavras em uma lista:")
for palavra in ["Ordem", "e", "Progresso"]:
    print(palavra)
Palavras em uma lista:
Ordem
e
Progresso

Em um dos exemplos anteriores, aprendemos a criar listas aninhadas em Python. Podemos usar in para percorrer esse tipo de lista também. Vejamos:

matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print("Matriz 3x3: ", matriz)

# A sintaxe abaixo será explicada posteriormente
print("Matriz impressa de outra forma:")
for lista in matriz:
    for elemento in lista:
        print(elemento, end=' ')
    print()
Matriz 3x3:  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Matriz impressa de outra forma:
1 2 3
4 5 6
7 8 9

Note que nos exemplos acima não tivemos que nos referir aos índices dos elementos da lista ao percorrê-la. Entretanto, há situações em que precisamos também dos índices por algum motivo. Nesses casos, podemos usar a funcão enumerate, como ilustrado abaixo:

frutas = ["morango", "uva", "banana", "pera"]
for i, fruta in enumerate(frutas):
    print("A fruta {} está no índice {}".format(fruta, i))
A fruta morango está no índice 0
A fruta uva está no índice 1
A fruta banana está no índice 2
A fruta pera está no índice 3

Quando estamos manipulando listas de números, pode ser interessante percorrer todos os números em um dado intervalo. Para fazer isso, usamos a função range(), como mostrado abaixo:

# Sintaxe da função range()

inicio = 0
fim = 100
passo = 10

# Convertendo um intervalo em uma lista.
print(list(range(inicio, fim, passo)))
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

Perceba que assim como no caso de slices de strings, o elemento final do intervalo (no caso acima, o 100) não é retornado pela função range(). O exemplo abaixo mostra isso melhor.

# Note que o 10 não é incluído no resultado.
print(list(range(0, 10, 1)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Além disso, se não fornecermos o passo, a função irá usar o valor padrão 1:

# O valor padrão para 'passo' é 1.
print(list(range(0, 10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Pesquisa linear

Um exemplo interessante de uso de laços for é a busca por um elemento em uma lista:

elemento = 10
estah_presente = False
lista = [1, 2, 7, 10, 15, 20, 50]
for item in lista:
  if item == elemento:
    estah_presente = True

if estah_presente:
  print("Elemento está na lista!")
else:
  print("Elemento não está na lista!")
Elemento está na lista!

Uma outra forma de pesquisar por um elemento em uma lista em Python é por meio do recurso in:

elemento = 10
lista = [1, 2, 7, 10, 15]
if elemento in lista:
  print("Está na lista!")
else:
  print("Não está na lista!")
Está na lista!

Esse tipo de pesquisa em listas é chamada pesquisa linear. Existe ainda um outro tipo de pesquisa, chamada pesquisa binária, que é mais eficiênte caso a lista esteja ordenada.

Não entraremos em detalhes sobre pesquisa binária no momento, mas caso você tenha interesse, leia a documentação do módulo bisect de Python.

Modificando listas

Em algumas situações, é preciso adicionar, remover ou modificar elementos em uma lista. Python nos provê recursos para fazer essas coisas. Veja:

# Cria uma lista vazia.
lista = []

# Adiciona elementos no final da lista.
lista.append("P")
lista.append("Y")
lista.append("T")
lista.append("H")
lista.append("O")
lista.append("N")

print(lista)
['P', 'Y', 'T', 'H', 'O', 'N']

É possível modificar elementos em uma lista. Dizemos que listas são tipos de dados mutáveis. Este conceito ficará mais interessante quando estudarmos tuplas, que são tipos de dados imutáveis.

lista = ['P', 'Y', 'T', 'H', 'O', 'N']
lista[1] = 'A'
lista[3] = 'R'
lista[4] = 'I'
lista[5] = 'A'

print(lista)
['P', 'A', 'T', 'R', 'I', 'A']

Python possui um outro recurso, chamado extend, para adicionar elementos ao final de uma lista. Vejamos como esse recurso funciona.

bolo = ['farinha de trigo', 'ovo', 'leite', 'manteiga']
bolo.append(['açucar', 'fermento'])
print(bolo)
['farinha de trigo', 'ovo', 'leite', 'manteiga', ['açucar', 'fermento']]

Perceba que o append adicionou a lista ['açucar', 'fermento'] à lista bolo. Mas talvez desejássemos adicionar ’açucar’ e ’fermento' como elementos da lista bolo, e não como uma lista dentro de uma lista. O extend nos permite fazer isso.

bolo = ['farinha de trigo', 'ovo', 'leite', 'manteiga']
bolo.extend(['açucar', 'fermento'])
print(bolo)
['farinha de trigo', 'ovo', 'leite', 'manteiga', 'açucar', 'fermento']

A função append sempre insere elementos no final da lista. Isso é bom por questões de desempenho. É mais rápido inserir elementos no final de uma lista porque não precisamos percorrer a lista procurando a posição onde o elemento deve ser inserido. Mas também é possível inserir elementos em posições específicas da lista, usando a função insert.

lista = ['P', 'T', 'H', 'N']
lista.insert(1, "Y")
lista.insert(4, "O")

print(lista)
['P', 'Y', 'T', 'H', 'O', 'N']

Como dissemos antes, listas são mutáveis, logo, é possível remover elementos delas.

lista = ['A', 'S', 'C', 'E', 'N', 'D', 'E', 'R']
print("Lista original: ", lista)

lista.remove("S")
print("Removendo um elemento: ", lista)
Lista original:  ['A', 'S', 'C', 'E', 'N', 'D', 'E', 'R']
Removendo um elemento:  ['A', 'C', 'E', 'N', 'D', 'E', 'R']

Para remover um elemento em uma posição específica, usamos del. Note a diferença em relação à função remove, que recebe como parâmetro o valor que desejamos remover.

lista = ['A', 'S', 'C', 'E', 'N', 'D', 'E', 'R']
print("Lista original: ", lista)

del lista[1]
print("Removendo um elemento: ", lista)
Lista original:  ['A', 'S', 'C', 'E', 'N', 'D', 'E', 'R']
Removendo um elemento:  ['A', 'C', 'E', 'N', 'D', 'E', 'R']

Vejamos agora como criar uma lista dentro de um laço de repetição. No exemplo abaixo, começamos com uma lista de frutas e desejamos criar uma lista com os nomes dessas frutas no plural. Vejamos uma forma de se fazer isso:

# Criando uma lista dentro de um for.
frutas = ['laranja', 'banana', 'abacate', 'manga']
plurais_frutas = []
for fruta in frutas:
    plural = fruta + 's'
    plurais_frutas += [plural]
print(plurais_frutas)
['laranjas', 'bananas', 'abacates', 'mangas']

Listas ordenadas

Em algumas situações, desejamos imprimir ou percorrer os elementos de uma lista em uma ordem específica. Para isso, podemos ordenar a lista. Python provê métodos para isso, conforme mostrado nos seguintes exemplos:

# Ordenando listas.
lista = [7, 25, 2, 3, 30, 7, 80, 100, -1, 15]
print("Lista não ordenada: ", lista)

lista.sort()
print("Lista ordenada: ", lista)

lista.sort(reverse=True)
print("Lista ordenada em ordem decrescente: ", lista)
Lista não ordenada:  [7, 25, 2, 3, 30, 7, 80, 100, -1, 15]
Lista ordenada:  [-1, 2, 3, 7, 7, 15, 25, 30, 80, 100]
Lista ordenada em ordem decrescente:  [100, 80, 30, 25, 15, 7, 7, 3, 2, -1]

Os exemplos acima mostram como ordenar listas em Python de forma forma que chamamos in place. Isso quer dizer que a lista foi ordenada sem que espaço adicional fosse usado. Python nos permite também ordenar uma cópia da lista original e retornar essa cópia ordenada da lista. Assim, a lista original permanece inalterada e uma nova lista ordenada é criada e retornada. Veja como isso funciona:

# Ordenando listas.
lista = [7, 2, 3, 7, -1, 9]
print("Lista original antes da ordenação: ", lista)

lista_ordenada = sorted(lista)
print("Lista ordenada: ", lista_ordenada)

print("A lista original permanece inalterada: ", lista)
Lista original antes da ordenação:  [7, 2, 3, 7, -1, 9]
Lista ordenada:  [-1, 2, 3, 7, 7, 9]
A lista original permanece inalterada:  [7, 2, 3, 7, -1, 9]

A menos que você precise tanto da lista original quanto de uma cópia dessa lista ordenada, recomenda-se o uso da ordenação in place, uma vez que ela não cria uma lista nova, e portanto usa menos espaço em memória.

Para mais detalhes sobre o funcionamento de listas em Python, digite help(list) na linha de comandos do interpretador Python.

Strings versus Listas

Como mencionado anteriomente, listas e strings possuem um funcionamento bem parecido em Python. Em muitas situações, entretanto, é preciso convertermos um tipo no outro. Python oferece meios de se fazer isso, como mostrado abaixo.

# Converte uma string em uma lista.
s = list("Ordem e Progresso")
print(s)

# Converte uma lista em uma string.
l = ['O', 'r', 'd', 'e', 'm', ' ', 'e', ' ', 'P', 'r', 'o', 'g', 'r', 'e', 's', 's', 'o']
print(''.join(l))
['O', 'r', 'd', 'e', 'm', ' ', 'e', ' ', 'P', 'r', 'o', 'g', 'r', 'e', 's', 's', 'o']
Ordem e Progresso

Em linguagens como C, uma string é basicamente uma lista (um arranjo, para ser mais exato) de caracteres. Em Python isso é diferente: uma lista de caracteres é diferente de uma string.

A principal diferença entre strings e listas em Python é que strings são imutáveis, enquanto listas são mutáveis. Quando tentamos modificar uma strings estamos, na verdade, criando uma nova string com as modificações feitas. Em se tratando de listas, as modificações são feitas sem que se crie uma nova cópia da lista.

Existem também algumas nuances com relação à conversão de strings para listas e de listas para strings. Nos exemplos acima, convertemos uma string (“Ordem e Progresso”) em uma lista de caracteres, e ao imprimir a lista vemos que a representação da string muda (ela passa a ser uma lista de caracteres).

No exemplo seguinte, convertemos a lista de caracteres de volta para uma string usando a função join, e vemos que a representação da string muda novamente (ela volta a ser uma string). Nestes casos, estamos criando novos objetos (strings ou listas) toda vez que convertemos um objeto em outro.

Fique atento a estas diferenças. A sintaxe para se manipular strings é muito parecida com a sintaxe para se manipular listas, mas strings e listas são coisas diferentes em Python.

Resumo de Operações em Listas

A tabela abaixo mostra um resumo dos principais operadores para manipulação de listas em Python.

Operações em listas

Operador Descrição Exemplo
+ concatena (soma) duas listas [1, 2, 3] + [4, 5] retorna [1, 2, 3, 4, 5]
* repete (replica) uma lista múltiplas vezes [0, 1] * 2 retorna [0, 1, 0, 1, 0, 1]
[i] i-ésimo elemento da lista [10, 20, 30][1] retorna 20
[i:j] sub-lista que vai dos índices i até j - 1 [1, 3, 5, 7, 9][1:3] retorna [3, 5]

Exercícios

  1. Escreva uma função que recebe uma lista ou string x e retorna x concatenada com ela mesma.
Clique para ver a solução
def concatena(x):
    return x + x

print(concatena('ha'))
haha

Outra opção é implementar a função assim:

def concatena(x):
    return 2 * x

print(concatena('ha'))
haha
  1. Escreva uma função que recebe como entrada uma lista de números e retorna True se um número passado como parâmetro está presente na lista.
Clique para ver a solução
def pesquisa_elemento1(numeros, numero_procurado):
    for numero in numeros:
      if numero == numero_procurado:
          return True
    return False

print(pesquisa_elemento1([1, 10, 20, 30, 50, 100], 30))
True

A função acima nada mais é que uma pesquisa linear na lista de entrada. Em outras palavras, percorremos a lista linearmente, elemento por elemento, e verificamos se cada o elemento corrente é o que estamos procurando. Caso seja, retornamos True. Caso o laço for termine e o elemento não tenha sido encontrado, retornamos False, pois percorremos toda a lista e não encontramos o elemento procurado.

Na função acima, escolhemos percorrer a lista usando um for que percorre cada elemento da lista. Poderíamos também tê-la escrito usando um for que percorre cada índice da lista, como abaixo:

def pesquisa_elemento2(numeros, numero_procurado):
    for indice in range(len(numeros)):
      if numeros[indice] == numero_procurado:
          return True
    return False

print(pesquisa_elemento2([1, 10, 20, 30, 50, 100], 30))
True

Como mencionamos acima, a função pesquisa_elemento1 nada mais é que uma pesquisa linear na lista de números de entrada. Como esse tipo de pesquisa é usada muito frequentemente, Python disponibiliza um mecanismo built-in (embutido na linguagem) para realizar esse tipo de pesquisa. Vejamos um exemplo:

def pesquisa_elemento3(numeros, numero_procurado):
    return numero_procurado in numeros

print(pesquisa_elemento3([1, 10, 20, 30, 50, 100], 30))
True

Note que o código acima usa o in para verificar se o número procurado está na lista de números recebida como entrada. Neste momento, você pode estar se perguntando qual é a diferença do in usado aqui para o in usado no for na função pesquisa_elemento1. Ocorre que, quando o in é usado juntamente com um for, como na função pesquisa_elemento1, ele é usado para percorrer uma lista de elementos um a um.

Mas, como vimos na função pesquisa_elemento3, o operador in também pode ser usado para checar se um elemento está em uma lista (ou em outra estrutura de dados).

É importante fazer essa distinção de uso do in, principalmente porque, como você verá por você mesmo, ele é muito usado no dia a dia.

  1. Escreva uma função que recebe como entrada uma lista ordenada de números e retorna o índice do primeiro elemento maior que um elemento limite. Se nenhum elemento da lista for maior que o limite desejado, retorne o valor -1.
Clique para ver a solução
def retorna_primeiro_maior(numeros, limite):
    i = 0
    while i < len(numeros):
      if numeros[i] > limite:
          return i
      i += 1
    return -1

print(retorna_primeiro_maior([1, 10, 20, 30, 50, 100], 10))
print(retorna_primeiro_maior([1, 10, 20, 30, 50, 100], 200))
2
-1
  1. Suponha que lhe seja fornecida uma lista de números inteiros positivos e um número $m$. Você pode assumir que todos os números na lista estão no intervalo $[0, m]$. Sua tarefa é dizer quais números aparecem duplicados na lista. Uma possível solução é percorrermos a lista uma vez para cada elemento presente e verificarmos se o elemento em questão aparece novamente na lista. Este raciocínio nos leva à seguinte solução.
Clique para ver a solução
def encontra_elementos_duplicados(lista, m):
    """
    Imprime os números que aparecem mais de uma vez na lista de entrada.
    É garantido que todos os números na lista de entrada estão no intervalo [0, m].
    """
    # Retorna zero se a lista de entrada estiver vazia.
    if not lista:
        return []

    # Procura por elementos repetidos na lista.
    duplicatas = []
    for i in range(len(lista)):
        for j in range(i + 1, len(lista)):
            if lista[i] == lista[j]:
                duplicatas.append(lista[j])

    # A saída do algoritmo é a lista de elementos repetidos.
    return duplicatas

elementos_duplicados = encontra_elementos_duplicados([1, 1, 20, 3, 3, 80, 7, 2, 25, 99, 75, 80], 100)
print("Elementos duplicados: ", elementos_duplicados)
Elementos duplicados:  [1, 3, 80]

Neste ponto, convém fazer algumas observações sobre a solução acima:

  • Nós usamos o comando if not lista para testar se a lista de entrada está vazia. Poderíamos ter usado o comando if len(lista) == 0 que teríamos obtido o mesmo resultado. Entretanto, o comando if not lista segue mais o estilo Python de fazer as coisas. Em outras palavras, dizemos que o comando if not lista é mais pythonico.
  • Nossa solução não explora o fato de que os números na lista estão no intervalo $[0, m]$.
  • Nossa solução percorre a lista (ou parte dela) várias vezes. A complexidade assintótica do nosso algoritmo é $O(n^2)$, em que $n$ é o tamanho da lista.

Seria possível criarmos um algoritmo mais eficiente se levarmos em consideração o fato de que os números na lista de entrada estão no intervalo $[0, m]$? Perceba que a solução acima não usa esse fato. Ela ignora completamente que os números na lista de entrada estão no intervalo $[0, m]$. Nossa próxima solução usará essa informação para reduzir a complexidade assintótica do algoritmo de busca por elementos duplicados.

Se usarmos o fato de que os números estão no intervalo $[0, m]$, podemos criar uma lista auxiliar com a frequência dos valores na lista de entrada. Todas as vezes que encontrarmos um valor na lista na lista de entrada, incrementamos sua frequência na lista auxiliar. Havendo terminado de processar a lista de entrada, percorremos a lista auxiliar e vemos se algum dos elementos possui frequência maior que 1. Este raciocínio nos leva à seguinte solução.

def encontra_elementos_duplicados_frequencia(lista, m):
    """
    Imprime os números que aparecem mais de uma vez na lista de entrada.
    É garantido que todos os números na lista de entrada estão no intervalo [0, m].
    """
    # Retorna zero se a lista de entrada estiver vazia.
    if not lista:
        return []

    # Procura por elementos repetidos na lista.
    tabela_frequencia = [0] * m
    duplicatas = []
    for elemento in lista:
        tabela_frequencia[elemento] += 1
        if tabela_frequencia[elemento] > 1:
            duplicatas.append(elemento)

    # A saída do algoritmo é a lista de elementos repetidos.
    return duplicatas

elementos_duplicados = encontra_elementos_duplicados_frequencia([1, 1, 20, 3, 3, 80, 7, 2, 25, 99, 75, 80], 100)
print("Elementos duplicados: ", elementos_duplicados)
Elementos duplicados:  [1, 3, 80]

Façamos uma análise dessa nova solução.

Note que usamos uma estrutura de dados adicional, a tabela_frequencia. O modo como construímos essa tabela pode parecer estranho a princípio, mas o que ele faz é criar uma lista de tamanho m com todas as posições preenchidas com o valor 0. Esta é uma forma bastante pythonica de se criar e inicializar uma lista com todos os elementos iguais.

  1. Listas em Python possuem o método reverse, que inverte o conteúdo da lista. Sua tarefa é implementar uma função chamada inverte, que possui a mesma funcionalidade básica da função reverse em Python. Por exemplo, dada a lista [1, 2, 3, 4, 5, 6, 7], sua função deve retornar [7, 6, 5, 4, 3, 2, 1].
Clique para ver a solução

Primeira pergunta importante: O que devemos fazer se a lista de entrada estiver vazia? Iremos simplesmente retornar uma lista vazia.

E o que fazer se a lista de elementos não estiver vazia?

Se a lista não estiver vazia, precisamos trocar o último elemento com o primeiro, trocar o penúltimo com o segundo, trocar o antepenúltimo com o terceiro, e assim por diante. Este raciocínio nos leva à seguinte solução.

def inverte(nums):
    """Inverte o conteúdo da lista de entrada."""
    inicio = 0
    fim = len(nums) - 1
    while inicio < fim:
        # Note a forma elegante de trocarmos o conteúdo das variáveis.
        nums[inicio], nums[fim] = nums[fim], nums[inicio]
        inicio += 1
        fim -= 1
    return nums


print(inverte([1, 2, 3, 4, 5, 6, 7]))
[7, 6, 5, 4, 3, 2, 1]

Nossa função inverte parece funcionar corretamente. Mas como saber se ela está de fato correta? Basicamente, existem duas formas. A primeira é provarmos matematicamente que a função está correta. Mas isso é tedioso e complexo na maioria dos casos. A segunda, que na verdade não prova a corretude da função, mas que aumenta nosso nível de confiança de que ela esteja certa, é testarmos nossa implementação em um número grande de casos. A função abaixo testa nossa função, comparando o resultado dela com o resultado da função reverse da biblioteca padrão de Python. Na função abaixo, note o uso de asserções.

import random

def confere_inversao(n, m):
    """Checa o resultado da função `inverte` em n listas de tamanho m geradas aleatoriamente."""
    for _ in range(n):
        # Cria uma lista com números aleatórios no intervalo [0, m].
        nums1 = [random.randint(0, m) for _ in range(m)]

        # Cria uma lista com os mesmos elementos de nums1.
        nums2 = list(nums1)

        # Inverte a lista nums1 usando a biblioteca padrão de Python.
        nums1.reverse()

        # Invoca nosso algoritmo para inverter nums2 e confere o resultado.
        assert(inverte(nums2) == nums1)

    print("Sucesso!")

# Checa o resultado do nosso algoritmo em 500 listas geradas aleatoriamente.
confere_inversao(500, 500)
Sucesso!

Na função confere_inversao, usamos a função assert. O que essa função faz é checar se a condição passada como parâmetro é verdadeira. Caso a condição não seja verdadeira, a função assert interrompe o programa prematuramente, gerando um erro. Como a função confere_inversao executou com sucesso, o resultado da nossa função inverte foi o mesmo da função reverse em todos os 500 casos gerados.

  1. Suponha que lhe seja fornecida uma lista de números. Sua tarefa é mover todos os zeros para o final da lista, preservando a ordem dos números diferentes de zero. Por exemplo, dada a lista [0, 1, 0, 3, 12], seu programa deve retornar [1, 3, 12, 0, 0].
Clique para ver a solução

Uma solução simples para esse problema é fazer o seguinte:

  1. Contar o número de zeros.
  2. Mover todos os números diferentes de zero para frente na lista, ignorando quaisquer zeros, uma vez que já sabemos quantos existem.
  3. Preencher as posições no final da lista com o número de zeros obtidos no primeiro passo.

O raciocínio acima nos leva à seguinte solução.

def move_zeros_tres_etapas(nums):
    # Etapa 1: Conta o número de zeros na lista de números.
    zeros = nums.count(0)
    if zeros == 0:
        return nums

    # Etapa 2: Move todos os números diferentes de zero para frente da lista.
    atual = 0
    for i, num in enumerate(nums):
        if num != 0:
            nums[atual] = nums[i]
            atual += 1

    # Etapa 3: Coloca os zeros no final da lista.
    tamanho = len(nums) - 1
    for i in range(zeros):
        nums[tamanho - i] = 0

    return nums

print(move_zeros_tres_etapas([0, 1, 0, 3, 12]))
[1, 3, 12, 0, 0]