Programa de gestión de tareas en Python
Hice un programa simple en Python
- almacena tareas en el archivo
- permite al usuario gestionarlos.
Me interesa si mi implementación es eficiente y si puedo hacer algo mejor. No estoy seguro de si cargar todos los datos del archivo en la memoria y guardarlos nuevamente en el archivo es una buena manera de implementar tareas de guardado de listas.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
CHOICE_LIST_TASKS = 1
CHOICE_ADD_TASK = 2
CHOICE_REMOVE_TASK = 3
CHOICE_QUIT = 4
def read_int(prompt='> ', errmsg='Invalid number!'):
num = None
while num is None:
try:
num = int(input(prompt))
except ValueError:
print(errmsg)
return num
def display_menu():
print('What do you want to do?')
print('[1] List all tasks')
print('[2] Add a new task')
print('[3] Delete task')
print('[4] Quit')
choice = None
file = None
if not os.path.exists('tasks'):
file = open('tasks', 'w+')
else:
file = open('tasks', 'r+')
tasks = []
for line in file.readlines():
tasks.append(line.strip())
while choice != CHOICE_QUIT:
display_menu()
choice = read_int()
if choice == CHOICE_LIST_TASKS:
if len(tasks) == 0:
print('Task list is empty!')
else:
print('Tasks:')
for x in tasks:
print(x)
elif choice == CHOICE_ADD_TASK:
desc = input('Which task do you want to do? ')
index = tasks.append(desc)
print('Successfully added a new task!')
elif choice == CHOICE_REMOVE_TASK:
if len(tasks) == 0:
print('Task list is empty!')
else:
for x in tasks:
print(tasks.index(x), x)
index = read_int('Which task do you want to delete? ')
try:
tasks.pop(index)
print('Successfully deleted task!')
except IndexError:
print('Please enter proper task number')
elif choice == CHOICE_QUIT:
print('Good bye!')
else:
print('Invalid choice!')
file = open('tasks', 'w+')
for x in tasks:
file.write(x + '\n')
Respuestas
Empecemos desde arriba.
CHOICE_LIST_TASKS = 1
CHOICE_ADD_TASK = 2
CHOICE_REMOVE_TASK = 3
CHOICE_QUIT = 4
Para mí está claro que desea crear variables y asignarles ciertos números enteros para no tener que usar números mágicos en ningún lado, felicitaciones por esto. Hay una manera mucho mejor de implementar esta idea en Python
Enumeraciones: Enum
Enumeraciones en Python
Una enumeración es un conjunto de nombres simbólicos
(miembros) ligados a valores constantes únicos
creando un Enum
class Choices(Enum):
list_task = 1
add_task = 2
remove_task = 3
quit = 4
Esto ya parece mucho más claro que las variables globales aleatorias. También puede recorrer la enumeración y mostrar sus valores con nombres .
print(Choices.add_task.name)
agregar tarea
print(Choices.add_task.value)
1
Usa más funciones
file = None
if not os.path.exists('tasks'):
file = open('tasks', 'w+')
else:
file = open('tasks', 'r+')
tasks = []
for line in file.readlines():
tasks.append(line.strip())
¿Por qué no podemos mover esto a una función llamada read_task_file()?
De esta manera, es extremadamente claro para mí, usted, usted después de un año leyendo proyectos antiguos, y cualquier otra persona que alguna vez lea este código, que esas pocas líneas de código leerán el archivo de tareas . Sin eso, uno debe leer el código a fondo y comprender su propósito.
def read_task_file():
file = None
if not os.path.exists('tasks'):
file = open('tasks', 'w+')
else:
file = open('tasks', 'r+')
tasks = []
for line in file.readlines():
tasks.append(line.strip())
return tasks
ahora conseguir las tareas es
tasks = read_task_file()
Esto mismo se aplica al siguiente conjunto de código, que permitirá al usuario ingresar su elección para que podamos realizarla.
Factorizar la parte en la que leemos una elección del usuario y la realizamos en una función se vería como
def read_task_choice():
display_menu()
choice = read_int()
if choice not in Choices._value2member_map_:
print("Invalid input! Please enter a correct choice\n")
read_task_choice()
Nota: Choices._value2member_map_serán todos los valores en una enumeración.
¡Esto ya se ve mucho más limpio! Eliminamos la necesidad de tenerlo en un bucle donde verificará todos los casos, la función solo regresa choicecuando hay una entrada válida.
Como dije, factorice el trabajo en funciones y todas ellas cuando sea necesario. He hecho lo mismo para los 4 trabajos principales (las 4 opciones).
Lista de tareas
def list_tasks(tasks):
if len(tasks) == 0:
print("Task list is empty!")
return None
for i in range(len(tasks)):
print(f"{i} {tasks[i]}")
Agregar nueva tarea
def add_new_task(tasks):
return input("What task would you like to finish ?: ")
tasks.append(task)
Quitar tarea
def remove_task(tasks):
if len(tasks) == 0:
print("Task list is already empty!")
return None
list_tasks(tasks)
task = int(input(("Which task would you like to remove: ?")))
if task < 0 or task >= len(tasks):
print("Invalid input! Please select an appropriate task")
remove_task(tasks)
tasks.pop(task)
Y finalmente
Actualizar archivo de tareas
def update_task_file(tasks):
file = open('tasks', 'w+')
for x in tasks:
file.write(x + '\n')
¿De qué sirve tener millones de funciones cuando puedo poner todo como ya lo he hecho? Ahora cuando tienes que jugar el juego
tasks = read_task_file()
while True:
choice = read_task_choice()
if choice == Choices.list_task.value:
list_tasks(tasks)
elif choice == Choices.add_task.value:
add_new_task(tasks)
elif choice == Choices.remove_task.value:
remove_task(tasks)
else:
break
update_task_file(tasks)
Esta es la principal ventaja de tener funciones fuertemente tipadas.
Limpiar la pantalla y esperar la entrada
Debido a la ausencia de gráficos, podemos ver que después de algunas elecciones el terminal se ve un poco extraño
Podemos mejorar esto simplemente borrando la pantalla después de cada elección y luego esperar a que el usuario presione una tecla
Limpiar la terminal
Hay algunas maneras. Una forma popular es usarla os.system("cls")si está en Windows, pero llamar a esto es bastante costoso y también hace que su programa dependa más de la plataforma.
Tu puedes hacer
print(chr(27) + "[2J")
Y esto también funcionará.
Esperando entrada
Agregar esta línea input("Press any key to continue...")esperará la respuesta del usuario antes de mostrar todas las opciones nuevamente, mejorando la experiencia.
Final
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
from enum import Enum
class Choices(Enum):
list_task = 1
add_task = 2
remove_task = 3
quit = 4
def read_int(prompt='> ', errmsg='Invalid number!'):
num = None
while num is None:
try:
num = int(input(prompt))
except ValueError:
print(errmsg)
return num
def display_menu():
print('What do you want to do?')
print('[1] List all tasks')
print('[2] Add a new task')
print('[3] Delete task')
print('[4] Quit')
def read_task_file():
file = None
if not os.path.exists('tasks'):
file = open('tasks', 'w+')
else:
file = open('tasks', 'r+')
tasks = []
for line in file.readlines():
tasks.append(line.strip())
return tasks
def update_task_file(tasks):
file = open('tasks', 'w+')
for x in tasks:
file.write(x + '\n')
def read_task_choice():
display_menu()
choice = read_int()
if choice not in Choices._value2member_map_:
print("Invalid input! Please enter a correct choice\n")
read_task_choice()
return choice
def add_new_task(tasks):
task = input("What task would you like to do: ")
tasks.append(task)
def list_tasks(tasks):
if len(tasks) == 0:
print("Task list is empty!")
return None
for i in range(len(tasks)):
print(f"{i} {tasks[i]}")
def remove_task(tasks):
if len(tasks) == 0:
print("Task list is already empty!")
return None
list_tasks(tasks)
task = int(input(("Which task would you like to remove: ?")))
if task < 0 or task >= len(tasks):
print("Invalid input! Please select an appropriate task")
remove_task(tasks)
tasks.pop(task)
def clear_screen():
print(chr(27) + "[2J")
def pause():
input("Press any key to continue...")
tasks = read_task_file()
while True:
clear_screen()
choice = read_task_choice()
if choice == Choices.list_task.value:
list_tasks(tasks)
elif choice == Choices.add_task.value:
add_new_task(tasks)
elif choice == Choices.remove_task.value:
remove_task(tasks)
else:
break
update_task_file(tasks)
pause()