Projeto Lista de Tarefas

A Ana vive esquecendo coisas: comprar pão, pagar a conta de luz, levar o Rex ao veterinário. Neste projeto, ela ganha um programa de tarefas no terminal, com um menu: adicionar, ver, concluir, remover e sair. É o primeiro projeto do guia que guarda dados: primeiro numa lista, enquanto o programa está aberto, e no fim num arquivo, para as tarefas continuarem lá amanhã.

Você vai praticar

O que vamos construir

A lista pronta, no terminal da Ana. Ela abre o programa, cadastra três tarefas, conclui uma e sai. O que vem depois de Escolha:, Nova tarefa: e Número da tarefa: foi digitado por ela. Depois, o cat mostra o arquivo que o programa gravou:

$ python3 tarefas.py
=== Lista de Tarefas da Ana ===
0 tarefa(s) carregada(s) de tarefas.txt.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Pagar a conta de luz
Tarefa adicionada: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Levar o Rex ao veterinário
Tarefa adicionada: Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 2
Concluída: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 0
Até mais!
$ cat tarefas.txt
[ ] Comprar pão
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário

No dia seguinte, a Ana abre o programa de novo, e as tarefas estão lá:

$ python3 tarefas.py
=== Lista de Tarefas da Ana ===
3 tarefa(s) carregada(s) de tarefas.txt.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 2
1. [ ] Comprar pão
2. [x] Pagar a conta de luz
3. [ ] Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 4
Número da tarefa: 1
Removida: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 0
Até mais!
$ cat tarefas.txt
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário

Quatro versões:

  1. O menu e a lista (while True, match, append, enumerate).
  2. Concluir e remover (dicionários, pop).
  3. Funções e número validado (def, isdigit, None).
  4. Guardar num arquivo (open com "w", FileNotFoundError).

Antes de começar

Este projeto usa o while True com match da Calculadora. Crie a pasta; o arquivo vai se chamar tarefas.py:

$ mkdir -p projetos/tarefas
$ cd projetos/tarefas

Versão 1 · O menu e a lista

Todo programa de menu tem o mesmo esqueleto: um laço que mostra as opções, lê a escolha e faz o que foi pedido, até a pessoa escolher sair. As tarefas ficam numa lista de textos.

tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append(titulo)
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            if not tarefas:
                print("Nenhuma tarefa ainda.")
            for numero, titulo in enumerate(tarefas, start=1):
                print(f"{numero}. {titulo}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
tarefas = []
A lista começa vazia, fora do laço. Se ficasse dentro, seria esvaziada a cada volta do menu.
print()
Um print sem nada escreve uma linha em branco: separa cada menu do anterior.
opcao = input("Escolha: ").strip()
A opção fica como texto ("1", "2"…), sem int(). Assim, se a pessoa digitar x, não há erro: cai no case _.
tarefas.append(titulo)
Acrescenta a tarefa no fim da lista (veja Adicionando Itens).
if not tarefas:
Uma lista vazia conta como falso: "se não há tarefas".
for numero, titulo in enumerate(tarefas, start=1):
enumerate ("enumerar") entrega, a cada volta, um par: o número e o item. O start=1 faz a contagem começar em 1, como as pessoas contam (veja abaixo).
case "0": … break
A única saída do laço.
$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> tarefas = ["Comprar pão", "Pagar a luz"]
>>> list(enumerate(tarefas, start=1))
[(1, 'Comprar pão'), (2, 'Pagar a luz')]
>>> list(enumerate(tarefas))
[(0, 'Comprar pão'), (1, 'Pagar a luz')]
>>> exit()

Sem o start=1, a contagem começaria em 0, que é como o Python numera as posições de uma lista (o primeiro item está na posição 0). Para mostrar à Ana, 1 é melhor. Guarde essa diferença: ela vai importar na versão 2.

$ python3 tarefas.py
=== Lista de Tarefas da Ana ===

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 2
Nenhuma tarefa ainda.

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 1
Nova tarefa: Pagar a conta de luz
Tarefa adicionada: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 1
Nova tarefa: Levar o Rex ao veterinário
Tarefa adicionada: Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 2
1. Comprar pão
2. Pagar a conta de luz
3. Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 5
Opção inválida.

1 - Adicionar tarefa
2 - Ver tarefas
0 - Sair
Escolha: 0
Até mais!

Missão (versão 1): a opção 2 mostra a lista "crua", com colchetes e aspas: ['Comprar pão', 'Pagar a conta de luz']. Troque o print(tarefas) por um for com enumerate(tarefas, start=1) que mostre 1. Comprar pão, 2. Pagar a conta de luz. A entrada da missão cadastra as duas tarefas, escolhe 2 e sai.

tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append(titulo)
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            print(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append(titulo)
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            if not tarefas:
                print("Nenhuma tarefa ainda.")
            for numero, titulo in enumerate(tarefas, start=1):
                print(f"{numero}. {titulo}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")

Versão 2 · Concluir e remover

Uma tarefa agora tem duas informações: o título e se já foi feita. Um texto só não basta; cada tarefa vira um dicionário com duas chaves, e a lista passa a ser uma lista de dicionários:

tarefas = [
    {"titulo": "Pagar a conta de luz", "feita": True},
    {"titulo": "Levar o Rex ao veterinário", "feita": False},
]
tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append({"titulo": titulo, "feita": False})
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            if not tarefas:
                print("Nenhuma tarefa ainda.")
            for numero, tarefa in enumerate(tarefas, start=1):
                marca = "x" if tarefa["feita"] else " "
                print(f"{numero}. [{marca}] {tarefa['titulo']}")
        case "3":
            numero = int(input("Número da tarefa: "))
            tarefa = tarefas[numero - 1]
            tarefa["feita"] = True
            print(f"Concluída: {tarefa['titulo']}")
        case "4":
            numero = int(input("Número da tarefa: "))
            tarefa = tarefas.pop(numero - 1)
            print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
LinhaO que faz
{"titulo": titulo, "feita": False}A tarefa nova: o título digitado e "feita" valendo False (ainda não foi feita).
marca = "x" if tarefa["feita"] else " "Um if em uma linha: "x" para as feitas, espaço para as outras. Vira [x] ou [ ].
f"{numero}. [{marca}] {tarefa['titulo']}"Dentro da f-string, a chave vai com aspas simples ('titulo'), porque a f-string já usa as duplas. (Desde o Python 3.12 dá para repetir as aspas duplas lá dentro, mas aspas diferentes funcionam em qualquer versão e são mais fáceis de ler.)
numero = int(input("Número da tarefa: "))Aqui é preciso um número de verdade, para achar a tarefa na lista.
tarefas[numero - 1]A Ana conta a partir de 1; a lista, a partir de 0. A tarefa 2 da tela está na posição 1 da lista: por isso o - 1.
tarefa["feita"] = TrueMuda o valor da chave "feita" dentro do dicionário que está na lista. Não é preciso "devolver" a tarefa para a lista: a variável tarefa e a lista apontam para o mesmo dicionário (veja Mutável × Imutável).
tarefas.pop(numero - 1)Tira da lista o item daquela posição e o devolve, para o programa poder dizer qual foi removido (veja Removendo Itens).
$ python3 tarefas.py
=== Lista de Tarefas da Ana ===

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Pagar a conta de luz
Tarefa adicionada: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Levar o Rex ao veterinário
Tarefa adicionada: Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 2
Concluída: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 4
Número da tarefa: 1
Removida: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 2
1. [x] Pagar a conta de luz
2. [ ] Levar o Rex ao veterinário

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 0
Até mais!

Repare na última listagem: depois de remover a tarefa 1 (Comprar pão), as outras subiram uma posição. A conta de luz, que era a 2, virou a 1. Os números da tela são só a posição atual na lista.

Quando dá errado na versão 2

Um número que não existe: IndexError
$ python3 tarefas.py
=== Lista de Tarefas da Ana ===

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 5
Traceback (most recent call last):
  File "/home/ana/projetos/tarefas/tarefas.py", line 26, in <module>
    tarefa = tarefas[numero - 1]
             ~~~~~~~^^^^^^^^^^^^
IndexError: list index out of range

Só existe a tarefa 1, e a Ana pediu a 5. IndexError: list index out of range = "erro de índice: a posição está fora da lista". A lista tem uma posição só (a 0), e o programa pediu a posição 4 (5 - 1). O sublinhado aponta o tarefas[numero - 1], na linha 26. Com pop, a mensagem seria parecida: IndexError: pop index out of range.

O número 0 conclui a tarefa errada, sem erro nenhum
$ python3 tarefas.py
=== Lista de Tarefas da Ana ===

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Pagar a conta de luz
Tarefa adicionada: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 0
Concluída: Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 2
1. [ ] Comprar pão
2. [x] Pagar a conta de luz

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: dois
Traceback (most recent call last):
  File "/home/ana/projetos/tarefas/tarefas.py", line 25, in <module>
    numero = int(input("Número da tarefa: "))
             ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'dois'

Este é o pior tipo de defeito: o programa não reclamou, mas fez a coisa errada. A Ana digitou 0 e a última tarefa (a conta de luz) foi concluída. O motivo: 0 - 1 é -1, e, numa lista, o índice -1 é válido: quer dizer "o último item" (o -2 é o penúltimo, e assim por diante; veja Acessando Itens). Logo depois, o dois por extenso derrubou o programa com o ValueError que você já conhece da Calculadora. A versão 3 cuida dos três casos.

Missão (versão 2): o menu oferece "3 - Concluir tarefa", mas o match não tem o case "3", e a opção cai em "Opção inválida.". Escreva o caso: leia o número com int(input("Número da tarefa: ")), pegue a tarefa em tarefas[numero - 1], ponha True na chave "feita" e mostre Concluída: …. A entrada da missão cadastra duas tarefas, conclui a 2 e lista.

tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append({"titulo": titulo, "feita": False})
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            if not tarefas:
                print("Nenhuma tarefa ainda.")
            for numero, tarefa in enumerate(tarefas, start=1):
                marca = "x" if tarefa["feita"] else " "
                print(f"{numero}. [{marca}] {tarefa['titulo']}")
        case "4":
            numero = int(input("Número da tarefa: "))
            tarefa = tarefas.pop(numero - 1)
            print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            tarefas.append({"titulo": titulo, "feita": False})
            print(f"Tarefa adicionada: {titulo}")
        case "2":
            if not tarefas:
                print("Nenhuma tarefa ainda.")
            for numero, tarefa in enumerate(tarefas, start=1):
                marca = "x" if tarefa["feita"] else " "
                print(f"{numero}. [{marca}] {tarefa['titulo']}")
        case "3":
            numero = int(input("Número da tarefa: "))
            tarefa = tarefas[numero - 1]
            tarefa["feita"] = True
            print(f"Concluída: {tarefa['titulo']}")
        case "4":
            numero = int(input("Número da tarefa: "))
            tarefa = tarefas.pop(numero - 1)
            print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")

Versão 3 · Funções e número validado

O match estava ficando enorme. Na versão 3, cada pedaço com nome próprio vira uma função: mostrar_menu, listar e, a mais importante, escolher_tarefa, que pergunta o número e só devolve uma posição se ela existir. Concluir e remover usam a mesma função, então a validação é escrita uma vez só. E o título vazio (só Enter) passa a ser recusado.

def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
def escolher_tarefa(tarefas):
Recebe a lista e devolve a posição da tarefa escolhida (de 0 em diante), ou None quando não dá.
if not tarefas: … return None
Lista vazia: nem pergunta.
if not texto.isdigit():
isdigit() ("é dígito?") dá True só se o texto tiver apenas algarismos. Recusa "dois", "" e também "-1" (o sinal de menos não é algarismo).
if not 1 <= numero <= len(tarefas):
O número tem de estar entre 1 e a quantidade de tarefas. É aqui que o 0 e o 5 são barrados.
return numero - 1
A conversão "número da tela → posição na lista" agora mora num lugar só.
if posicao is not None:
No programa principal: só conclui ou remove se veio uma posição. Atenção: if posicao: estaria errado, porque a posição 0 (a primeira tarefa) conta como falso. Para None, use sempre is/is not.
if titulo:
Texto vazio conta como falso: recusa o título vazio.
$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> "12".isdigit()
True
>>> "-1".isdigit()
False
>>> " 3".isdigit()
False
>>> exit()

O espaço também conta: " 3".isdigit() é False. Por isso o texto passa pelo strip() antes do teste.

$ python3 tarefas.py
=== Lista de Tarefas da Ana ===

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Nenhuma tarefa ainda.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: 
A tarefa precisa de um nome.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Comprar pão
Tarefa adicionada: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 5
Não existe a tarefa 5. Escolha de 1 a 1.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 0
Não existe a tarefa 0. Escolha de 1 a 1.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: dois
"dois" não é um número.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 3
Número da tarefa: 1
Concluída: Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 2
1. [x] Comprar pão

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 0
Até mais!
Chave com o nome errado: KeyError

Com dicionários, um erro de digitação no nome da chave não passa despercebido:

$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> tarefa = {"titulo": "Comprar pão", "feita": False}
>>> tarefa["titlo"]
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    tarefa["titlo"]
    ~~~~~~^^^^^^^^^
KeyError: 'titlo'
>>> exit()

KeyError: 'titlo' = "erro de chave: não existe a chave 'titlo'". (No modo interativo, o "arquivo" se chama <python-input-1>: é a segunda entrada digitada nessa sessão, contando a partir de 0.) Como consertar: confira o nome da chave, letra por letra. Num programa grande, o mesmo erro aparece com o nome do arquivo e a linha.

Missão (versão 3): a função escolher_tarefa ainda não valida nada, e o 5 da entrada derruba o programa com IndexError. Escreva o corpo dela como na versão 3: lista vazia, isdigit() e o teste 1 <= numero <= len(tarefas) com a mensagem Não existe a tarefa 5. Escolha de 1 a 1. A entrada da missão cadastra uma tarefa, tenta concluir a 5, depois a 0, depois a 1, e lista.

def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    return int(input("Número da tarefa: ")) - 1


tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


tarefas = []

print("=== Lista de Tarefas da Ana ===")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")

Versão 4 · Guardar num arquivo

Até a versão 3, tudo o que a Ana cadastra some quando o programa fecha: a lista vive na memória do computador só enquanto o programa roda. Para as tarefas sobreviverem, a versão 4 as grava num arquivo de texto, tarefas.txt, uma por linha, no mesmo formato da tela:

[ ] Comprar pão
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário

Duas funções novas: carregar(), que lê o arquivo quando o programa abre, e salvar(tarefas), chamada depois de cada mudança (adicionar, concluir, remover). O resto é a versão 3.

ARQUIVO = "tarefas.txt"


def carregar():
    """Lê as tarefas do arquivo. Se ele ainda não existe, começa com a lista vazia."""
    tarefas = []
    try:
        with open(ARQUIVO, encoding="utf-8") as arquivo:
            for linha in arquivo:
                linha = linha.rstrip("\n")
                if linha.startswith("[x] "):
                    tarefas.append({"titulo": linha[4:], "feita": True})
                elif linha.startswith("[ ] "):
                    tarefas.append({"titulo": linha[4:], "feita": False})
    except FileNotFoundError:
        pass  # primeira vez: ainda não há arquivo
    return tarefas


def salvar(tarefas):
    """Grava todas as tarefas no arquivo, uma por linha."""
    with open(ARQUIVO, "w", encoding="utf-8") as arquivo:
        for tarefa in tarefas:
            marca = "x" if tarefa["feita"] else " "
            arquivo.write(f"[{marca}] {tarefa['titulo']}\n")


def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


print("=== Lista de Tarefas da Ana ===")
tarefas = carregar()
print(f"{len(tarefas)} tarefa(s) carregada(s) de {ARQUIVO}.")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
                salvar(tarefas)
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
                salvar(tarefas)
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
                salvar(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
PeçaO que faz
open(ARQUIVO, "w", encoding="utf-8")Abre para escrever ("w", de write). Atenção: o "w" apaga o que havia no arquivo e começa do zero. Aqui é o que queremos: a cada salvar, o arquivo recebe a lista inteira, atualizada (veja Escrevendo Arquivos).
arquivo.write(f"[{marca}] …\n")Escreve uma linha. O write não pula linha sozinho, como o print: o \n no fim é obrigatório.
with open(ARQUIVO, encoding="utf-8") as arquivo:Sem o "w", abre para ler (o padrão). O with fecha o arquivo no fim do bloco.
linha.rstrip("\n")Tira só o \n do fim da linha (o r é de right, "direita"). Um strip() comum também tiraria espaços que façam parte do título.
linha.startswith("[x] ")"Começa com?": decide se a tarefa estava feita.
linha[4:]Uma fatia: tudo a partir da posição 4, ou seja, o texto depois de [x] (quatro caracteres: colchete, x, colchete, espaço).
except FileNotFoundError: passNa primeira vez, o arquivo ainda não existe, e o open daria FileNotFoundError. Aqui isso não é um problema: começa com a lista vazia. O pass ("passar") é a instrução que não faz nada.

As duas sessões do começo da página são desta versão: na primeira, o programa diz 0 tarefa(s) carregada(s) (o arquivo ainda não existia) e, ao sair, o tarefas.txt já tem as três tarefas; na segunda, ele carrega as três, e a remoção também vai para o arquivo.

Por que salvar depois de cada mudança, e não só ao sair?

Se o programa fosse interrompido no meio (um Ctrl+C, um erro, a bateria do notebook), tudo o que a Ana fez desde que abriu o programa se perderia. Salvando a cada mudança, o arquivo está sempre em dia. Com poucas tarefas, regravar o arquivo inteiro é instantâneo.

Quando dá errado na versão 4

"a" no lugar de "w": as tarefas se duplicam

O modo "a" (de append, "acrescentar") escreve no fim do arquivo, sem apagar o que já havia. Parece mais seguro, mas olhe o que acontece com a função salvar, que grava a lista inteira:

$ python3 tarefas.py
=== Lista de Tarefas da Ana ===
2 tarefa(s) carregada(s) de tarefas.txt.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Regar as plantas
Tarefa adicionada: Regar as plantas

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 0
Até mais!
$ cat tarefas.txt
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário
[ ] Regar as plantas

O arquivo tinha 2 tarefas; o programa carregou as 2, a Ana acrescentou uma, e o salvar escreveu as 3 depois das 2 que já estavam lá: 5 linhas, com repetições. Na próxima vez, seriam 5 carregadas, e o arquivo cresceria a cada mudança. Não há traceback, porque para o Python está tudo certo. Como consertar: quando a função grava a lista inteira, use "w".

Esquecer o modo: io.UnsupportedOperation: not writable
$ python3 tarefas.py
=== Lista de Tarefas da Ana ===
5 tarefa(s) carregada(s) de tarefas.txt.

1 - Adicionar tarefa
2 - Ver tarefas
3 - Concluir tarefa
4 - Remover tarefa
0 - Sair
Escolha: 1
Nova tarefa: Lavar a louça
Tarefa adicionada: Lavar a louça
Traceback (most recent call last):
  File "/home/ana/projetos/tarefas/tarefas.py", line 75, in <module>
    salvar(tarefas)
    ~~~~~~^^^^^^^^^
  File "/home/ana/projetos/tarefas/tarefas.py", line 25, in salvar
    arquivo.write(f"[{marca}] {tarefa['titulo']}\n")
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
io.UnsupportedOperation: not writable

Sem o "w", o open abriu o arquivo só para leitura, e o write falhou: io.UnsupportedOperation: not writable = "operação não suportada: (o arquivo) não aceita escrita". O traceback tem dois trechos: o programa principal chamou salvar na linha 75, e dentro dela o write da linha 25 deu o erro. (Repare que o programa chegou a dizer "Tarefa adicionada", mas não conseguiu gravar.) Como consertar: open(ARQUIVO, "w", encoding="utf-8").

Missão (versão 4, salvar): a função salvar ainda não faz nada, e o tarefas.txt nunca é criado. Escreva o corpo dela: abra o arquivo com "w" e encoding="utf-8" e, para cada tarefa, grave [x] título ou [ ] título com \n no fim. A entrada da missão cadastra duas tarefas e conclui a primeira; a missão confere o arquivo tarefas.txt depois que o programa termina.

ARQUIVO = "tarefas.txt"


def carregar():
    """Lê as tarefas do arquivo. Se ele ainda não existe, começa com a lista vazia."""
    tarefas = []
    try:
        with open(ARQUIVO, encoding="utf-8") as arquivo:
            for linha in arquivo:
                linha = linha.rstrip("\n")
                if linha.startswith("[x] "):
                    tarefas.append({"titulo": linha[4:], "feita": True})
                elif linha.startswith("[ ] "):
                    tarefas.append({"titulo": linha[4:], "feita": False})
    except FileNotFoundError:
        pass  # primeira vez: ainda não há arquivo
    return tarefas


def salvar(tarefas):
    """Grava todas as tarefas no arquivo, uma por linha."""
    pass  # escreva aqui: abra o arquivo com "w" e grave uma linha por tarefa


def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


print("=== Lista de Tarefas da Ana ===")
tarefas = carregar()
print(f"{len(tarefas)} tarefa(s) carregada(s) de {ARQUIVO}.")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
                salvar(tarefas)
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
                salvar(tarefas)
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
                salvar(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
ARQUIVO = "tarefas.txt"


def carregar():
    """Lê as tarefas do arquivo. Se ele ainda não existe, começa com a lista vazia."""
    tarefas = []
    try:
        with open(ARQUIVO, encoding="utf-8") as arquivo:
            for linha in arquivo:
                linha = linha.rstrip("\n")
                if linha.startswith("[x] "):
                    tarefas.append({"titulo": linha[4:], "feita": True})
                elif linha.startswith("[ ] "):
                    tarefas.append({"titulo": linha[4:], "feita": False})
    except FileNotFoundError:
        pass  # primeira vez: ainda não há arquivo
    return tarefas


def salvar(tarefas):
    """Grava todas as tarefas no arquivo, uma por linha."""
    with open(ARQUIVO, "w", encoding="utf-8") as arquivo:
        for tarefa in tarefas:
            marca = "x" if tarefa["feita"] else " "
            arquivo.write(f"[{marca}] {tarefa['titulo']}\n")


def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


print("=== Lista de Tarefas da Ana ===")
tarefas = carregar()
print(f"{len(tarefas)} tarefa(s) carregada(s) de {ARQUIVO}.")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
                salvar(tarefas)
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
                salvar(tarefas)
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
                salvar(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")

Na caixa abaixo, o tarefas.txt já existe, numa aba ao lado do programa: é o arquivo que a Ana deixou ontem.

Missão (versão 4, carregar): agora o contrário: a função carregar não lê o arquivo, e o programa diz 0 tarefa(s) carregada(s) mesmo com o tarefas.txt da aba. Escreva a leitura como na versão 4 (com try, with open, startswith e linha[4:], e o except FileNotFoundError). A entrada da missão só lista e sai.

ARQUIVO = "tarefas.txt"


def carregar():
    """Lê as tarefas do arquivo. Se ele ainda não existe, começa com a lista vazia."""
    tarefas = []
    # escreva aqui: leia o arquivo e acrescente cada tarefa na lista
    return tarefas


def salvar(tarefas):
    """Grava todas as tarefas no arquivo, uma por linha."""
    with open(ARQUIVO, "w", encoding="utf-8") as arquivo:
        for tarefa in tarefas:
            marca = "x" if tarefa["feita"] else " "
            arquivo.write(f"[{marca}] {tarefa['titulo']}\n")


def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


print("=== Lista de Tarefas da Ana ===")
tarefas = carregar()
print(f"{len(tarefas)} tarefa(s) carregada(s) de {ARQUIVO}.")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
                salvar(tarefas)
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
                salvar(tarefas)
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
                salvar(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")
[x] Pagar a conta de luz
[ ] Levar o Rex ao veterinário
ARQUIVO = "tarefas.txt"


def carregar():
    """Lê as tarefas do arquivo. Se ele ainda não existe, começa com a lista vazia."""
    tarefas = []
    try:
        with open(ARQUIVO, encoding="utf-8") as arquivo:
            for linha in arquivo:
                linha = linha.rstrip("\n")
                if linha.startswith("[x] "):
                    tarefas.append({"titulo": linha[4:], "feita": True})
                elif linha.startswith("[ ] "):
                    tarefas.append({"titulo": linha[4:], "feita": False})
    except FileNotFoundError:
        pass  # primeira vez: ainda não há arquivo
    return tarefas


def salvar(tarefas):
    """Grava todas as tarefas no arquivo, uma por linha."""
    with open(ARQUIVO, "w", encoding="utf-8") as arquivo:
        for tarefa in tarefas:
            marca = "x" if tarefa["feita"] else " "
            arquivo.write(f"[{marca}] {tarefa['titulo']}\n")


def mostrar_menu():
    print()
    print("1 - Adicionar tarefa")
    print("2 - Ver tarefas")
    print("3 - Concluir tarefa")
    print("4 - Remover tarefa")
    print("0 - Sair")


def listar(tarefas):
    """Mostra as tarefas numeradas a partir de 1, com [x] nas feitas."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
    for numero, tarefa in enumerate(tarefas, start=1):
        marca = "x" if tarefa["feita"] else " "
        print(f"{numero}. [{marca}] {tarefa['titulo']}")


def escolher_tarefa(tarefas):
    """Pergunta o número de uma tarefa e devolve a posição dela na lista (ou None)."""
    if not tarefas:
        print("Nenhuma tarefa ainda.")
        return None
    texto = input("Número da tarefa: ").strip()
    if not texto.isdigit():
        print(f'"{texto}" não é um número.')
        return None
    numero = int(texto)
    if not 1 <= numero <= len(tarefas):
        print(f"Não existe a tarefa {numero}. Escolha de 1 a {len(tarefas)}.")
        return None
    return numero - 1


print("=== Lista de Tarefas da Ana ===")
tarefas = carregar()
print(f"{len(tarefas)} tarefa(s) carregada(s) de {ARQUIVO}.")
while True:
    mostrar_menu()
    opcao = input("Escolha: ").strip()

    match opcao:
        case "1":
            titulo = input("Nova tarefa: ").strip()
            if titulo:
                tarefas.append({"titulo": titulo, "feita": False})
                print(f"Tarefa adicionada: {titulo}")
                salvar(tarefas)
            else:
                print("A tarefa precisa de um nome.")
        case "2":
            listar(tarefas)
        case "3":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefas[posicao]["feita"] = True
                print(f"Concluída: {tarefas[posicao]['titulo']}")
                salvar(tarefas)
        case "4":
            posicao = escolher_tarefa(tarefas)
            if posicao is not None:
                tarefa = tarefas.pop(posicao)
                print(f"Removida: {tarefa['titulo']}")
                salvar(tarefas)
        case "0":
            print("Até mais!")
            break
        case _:
            print("Opção inválida.")

Formas diferentes de fazer

Validar o número: isdigit() ou try/except

A versão 3 testou o texto antes de converter. A Calculadora fez o contrário: tentou converter e tratou o erro. As duas funcionam aqui:

try:
    numero = int(texto)
except ValueError:
    print(f'"{texto}" não é um número.')
    return None
FormaQuando usar
texto.isdigit() antes do int()Números inteiros sem sinal, como o número de uma tarefa. Fácil de ler: "se não for só algarismos…".
try: int(texto) / except ValueErrorQuando o número pode ter sinal ou casas decimais ("-3", "2.5" com float). É o jeito mais comum em Python: "tente, e trate se der errado".

Onde guardar as tarefas

FormaQuando usar
Texto, uma linha por item ([x] Comprar pão)Dados simples. Dá para abrir e editar o arquivo à mão. Foi a escolha deste projeto.
CSVVárias colunas por item (título, data, prioridade), e o arquivo abre numa planilha.
JSONGrava listas e dicionários do Python quase como estão, sem você inventar um formato. É o que o próximo projeto, a Agenda de Contatos, usa.
Banco de dados (sqlite3)Muitos dados, buscas e mudanças frequentes.

Ideias para continuar

  • Uma opção "5 - Ver só as pendentes", com um for que pule as feitas (ou uma list comprehension).
  • Editar o título de uma tarefa (use escolher_tarefa de novo).
  • Mostrar no fim da listagem quantas faltam: 3 tarefa(s), 1 feita(s).
  • Um título com [x] no começo confunde o carregar? Experimente cadastrar a tarefa [x] teste e abrir o programa de novo.

Referência rápida do projeto

EscritaPara que serviu
while True: + match opcao:O menu que se repete até a opção de sair
tarefas.append(item)Acrescentar no fim da lista
enumerate(tarefas, start=1)Numerar a partir de 1 ao percorrer
{"titulo": t, "feita": False}Uma tarefa com duas informações
tarefas[numero - 1]["feita"] = TrueConcluir: número da tela → posição da lista; mudar a chave
tarefas.pop(posicao)Remover e receber o item removido
texto.isdigit(), 1 <= numero <= len(tarefas)Validar o número escolhido
if posicao is not None:Conferir se a função devolveu uma posição (a 0 também é válida)
open(nome, "w", encoding="utf-8") + write(… + "\n")Gravar o arquivo do zero
try: open(nome) … except FileNotFoundError:Ler o arquivo, se ele existir
linha.rstrip("\n"), linha.startswith("[x] "), linha[4:]Desmontar cada linha do arquivo
SintomaCausa provável
IndexError: list index out of rangeNúmero de tarefa maior que a lista: valide antes
O 0 conclui (ou remove) a última tarefa0 - 1 é -1, o último item: exija numero >= 1
ValueError: invalid literal for int() …Texto que não é número no int()
KeyError: 'titlo'Nome da chave do dicionário escrito errado
A primeira tarefa nunca é concluídaif posicao: no lugar de if posicao is not None: (a posição 0 conta como falso)
As tarefas se repetem no arquivoModo "a" numa função que grava a lista inteira: use "w"
io.UnsupportedOperation: not writableopen sem o "w"
As tarefas somem ao fechar o programaFaltou chamar salvar (ou ele grava num arquivo e o carregar lê outro)

Exercícios

1. Numerar a partir de 1

Complete:

for numero, tarefa in enumerate(tarefas, ):

2. Remover e receber

Complete o método que tira a tarefa da posição posicao e a devolve:

tarefa = tarefas.(posicao)

3. O modo de escrita

Complete o modo que apaga o arquivo e escreve do zero:

with open("tarefas.txt", "", encoding="utf-8") as arquivo:

4. Na versão 2, a Ana digitou 0 como número da tarefa. O que aconteceu?

5. Por que o programa principal da versão 3 usa if posicao is not None: e não if posicao:?

6. Na primeira vez que a versão 4 roda, o tarefas.txt não existe. O que acontece?


Resumo

  • Um programa de menu é um while True que mostra as opções, lê a escolha (como texto) e usa match; o break só no "sair".
  • A lista fica fora do laço. append acrescenta, pop(posição) remove e devolve.
  • A tela conta a partir de 1, a lista a partir de 0: enumerate(…, start=1) para mostrar, numero - 1 para achar.
  • O índice -1 é válido (o último item): um 0 digitado vira -1 e erra calado. Valide com 1 <= numero <= len(lista).
  • Um item com várias informações vira um dicionário; mudar a chave muda o item que está na lista.
  • Para os dados sobreviverem, grave num arquivo: "w" regrava tudo, "a" acrescenta no fim. Leia com try e trate o FileNotFoundError da primeira vez.