Diseña una arquitectura clara en Python con orientación a objetos para un CLI: separa interfaz, lógica de negocio y modelos. Aquí verás cómo modelar un cliente, generar IDs con UUID4, convertir objetos a diccionario y persistir datos en CSV con un servicio dedicado. Todo con código conciso y listo para crecer.
¿Cómo organizar la arquitectura de la aplicación con paquetes y módulos?
En aplicaciones reales, separar responsabilidades acelera el desarrollo y evita errores. Se propone dividir en tres capas: interfaz (comandos del CLI), lógica de negocio (servicios) y abstracciones (modelos/objetos con los que se interactúa).
¿Qué estructura de archivos y paquetes usar en clientes?
Paquete clients con el módulo commands.
Script inicial para ejecutar el CLI.
setup.py para instalar la aplicación en el sistema.
Nuevos archivos: models.py y services.py dentro de clients.
¿Qué papel cumple la interfaz de CLI en comandos?
Define cómo el software interactúa con el exterior.
En CLI, son los comandos que el usuario ejecuta.
Si fuese web: la interfaz gráfica. Si fuera API: los endpoints abiertos.
¿Dónde ubicar la lógica de negocio y las abstracciones?
Lógica de negocio en servicios: abrir y escribir archivos, operaciones de lista y actualización.
Abstracciones en modelos: objetos como el cliente representados con clases.
¿Cómo modelar al cliente con clases, self, UUID y schema?
El objeto cliente concentra datos y utilidades para persistir. Se resalta el uso de self, la generación de UID con el módulo estándar de Python y una representación tipo diccionario para escribir en CSV.
Clase cliente con init y primer parámetro self en métodos de instancia.
Atributos: name, company, email, position y UID.
UID opcional: si no viene, se genera con UUID4 (estándar de la industria).
Conversión a diccionario con vars para escribir a disco en CSV.
Decorador @staticmethod para exponer el schema (columnas) sin instanciar la clase.
Nota sobre lógica booleana: se menciona el uso de all y que None evalúa a falso.
# models.pyimport uuid
classCliente:def__init__(self, name, company, email, position, uid=None): self.name = name
self.company = company
self.email = email
self.position = position
# si llega uid se usa; de lo contrario se genera uno único self.uid = uid or uuid.uuid4()defto_dict(self):# representación del objeto como diccionario, útil para CSVreturnvars(self)@staticmethoddefschema():# columnas que representan al objeto en datos tabularesreturn["name","company","email","position","uid"]
¿Cómo implementar el servicio de clientes y escribir en CSV?
La clase de servicio centraliza la lógica de negocio: sabe dónde guardar, en qué modo abrir y cómo transformar datos a filas tabulares. Así, los comandos solo orquestan y el servicio hace el trabajo.
¿Qué responsabilidades tiene ClientService en la creación?
Recibe el nombre de la tabla: el archivo CSV destino.
Abre el archivo en modo append para añadir nuevas filas.
Usa csv.DictWriter con fieldnames obtenidos de Cliente.schema() sin instanciar.
Estas últimas clases… resultaron muy confusas… Yo soy desarrollador en .Net y Java, y creo que el profesor en lo referente a la programación orientada a objetos y la creación de paquetes presenta muchos conceptos con muy poca explicación; por ejemplo el uso del decorador @staticmethod para indicar que una clase es estática lo introduce sin una explicación muy clara.
Estoy parcialmente de acuerdo, creo que una sección del curso enfocada a dar un vistazo mas a fondo a aspectos de programación funcional y orientada a objetos no hubiera estado de mas en este curso, sin embargo es un curso de Python, y no de programación sobre estos paradigmas. Igual espero que tomen en cuenta estas cosas, ya que a pesar de que uno puede investigar por si solo, deberían de tratar dejar bien claro todo lo que se ve en el curso por igual. Sobre el profesor la verdad ninguna queja, me parece que lo ha hecho bien, desconozco si los profesores son los que planean el curso, pero de ser así solo seria el detalle de profundizar mas.
muy de acuerdo contigo @chincapie, hay algunos conceptos que estan en el aire, y tu comentario es constructivo para con la clase, pero decir que es "un completo asco", de ninguna manera suma como aporte, no soy del equipo de platzi, pero creo que el otro comentario no aporta en nada, en fin, lo mio solo es un comentario, por lo demas el curso(a mi parecer) es muy bueno. Saludos.
Platzi debería tener una opción para calificar cada clase, con esto les seria mas facil hacer seguimiento a las clases que no están funcionando o con las que la comunidad no se siente conforme.
Arrancó lindo el curso, pero llegó un momento que pensó que lo más importante es manejar un sistema UNIX y no el lenguaje en sí.. éste curso fue en picada hacia abajo.
Esta clase parece una carrera contra el tiempo. Creo que podría tomarse un poco mas de tiempo explicando los pasos, sobre todo cuando construye las clases.
¡Hola @datalex! Tu feedback nos sirve mucho para mejorar. En cada actualización de cursos de Platzi siempre vas a encontrar más calidad, y no va a ser distinto con la de Python 😉
Hola! Te entiendo en ocasiones hay cursos pesados, me ha pasado con este curso también, lo que yo hago es tratar de ver la clase y tomar notas para entender lo que dice el profe. Recuerda que estos cursos tienen mucha información en poco tiempo pero la ventaja es que los podemos ver cuantas veces queramos.
Cuéntanos también, ¿Qué es lo que te confunde y hace que no puedas seguir?
Yo pienso que el profesor demuestra conocimientos profundos en todo momento, de temas muy complicados, al igual que a ustedes me resulta confuso pero trato de apoyame en libros al repecto e investigacion en internet, no es la idea ya que al adquirir un curso esperas que todo este claro pero, al entender las bases investigar y ensayar todo va tomando forma de a poquitos, el curso bueno, el profe conocimientos profundos y definidos, yo pondria el pero en la estructuracion del curso y propongo se sumen mas notas de apoyo escritas al curso y apoyo de herramientas como jupyter que harian mucho mas facil esto que solo videos, no hay forma de aprender sin ensayar y solo video lo hace dificil.
Para los que se pregunten el comando que el profesor usa para mostrar la estructura de archivos tiene una opcion -I donde indica que carpetas quiere ignorar. Para el caso espesifico de él esta ignorando _ _ pycache _ _, que es donde se guardan los archivos compilados por python; esta ignorando *.egg-info, que es donde se guarda la información de la instalación del programa y esta ignorando la carpeta venv, que es donde estan los archivos y carpetas relativos al ambiente virtual. Para mi caso tambien estoy ignorando la carpeta que crea vscode para que la estructura de archivos me quede más limpia y legible.
.
El comando resultante queda realmente largo para escribirlo constantemente, es recomendable guardarlo en un alias, bien sea un alias temporal para la sesion abierta del terminal o escribirlo en el archivo .bashrc que esta en el home.
Uff excelente aporte, deduje lo que mencionas después de ver que pasaba al solo usar tree dentro del entorno virtual y ¡PUM! un chorizote de árbol. Luego ya con lo implementado por el profe es más sencillo.
.
Saludos
El problema no esta en el profesor, ya que explica y se le entiende perfectamente; el problema radica en el contenido y estructuración del curso que presenta vacíos y confusiones entre cada clase.
Le falta estructurar las clases por que da saltos de información demasiado grandes y es complicado seguir el paso si no se tienen conocimientos de programación
Investigando un poco di con que existen classmethods y staticmethods y la diferencia me cuesta mucho entenderla. Aquí un link sobre dicha diferencia, pero está en inglés y me cuesta entenderlo con temas tan complejos.(). Apoyo 😦
Considero que la clase no estuvo a la altura de lo que pretende explicar, así que les voy a explicar lo que entendí sobre un tema que se pasó por alto. Si me equivoco en algo espero puedan corregirme:
@classmethods y @staticmethods son decoradores que se suele usar sobre las funciones de una clase.
Cuando trabajamos con clases, los métodos regulares (las funciones de toda la vida) pasan automáticamente una instancia como primer argumento (la llamamos self).
@classmethod automáticamente pasa una instancia referida a la clase como primer argumento (la llamamos cls y se comporta algo parecido a self) ejemplo:
#suponemos que estamos dentro de una clase y estamos definiendo sus métodos
@classmethod
def from_string(cls ,emp_string): #notese el cls
first, last, pay = emp_strsplit('-')
return cls(first,last,pay) #regreso los mismos valores que contiene el init de la clase, es como si cls emulara ser la clase
#no es requisito indispensable retornar cls, se pueden hacer otras cosas sin retornar.
#no se enrieden en los detalles del código, lo importante es el comportamiento de @classmethod y dejo link del vídeo al final para que puedan ver el ejemplo completo.
@staticmethod no pasa ninguna instancia automáticamente, casi son como los métodos regulares, pero guardan alguna conexión lógica con la clase.
Los métodos regulares necesitan usar el self para cumplir algún objetivo, @classmethod necesita el cls para cumplir algún objetivo, mientras que a @staticmethod lo vamos a usar en aquellos métodos (funciones) en los que no se requiera ni de self ni de cls. Un ejemplo es el que usa David en el código de la clase.
Este tema lo entendí gracias a: https://www.youtube.com/watch?v=rq8cL2XMM5M
Está en ingles, les sugiero que lo vean ya que seguramente explica mejor que yo el tema y van a poder ver un ejemplo completo. Y si no entiendes ingles, espero haberte ayudado con mi explicación del tema. Saludos
David Aroesti podrías anexar una descripción de que hace cada elemento del árbol de la API???, para tener mas claridad de como funciona cada scrip y módulo, dentro del sistema general
Para hacer un buen repaso de los conceptos de python es un buen curso pero de acá para adelante es mejor buscar por otros lados, ya se pierden los conceptos que se llevaban con tiempo y la curva de aprendizaje tiene saltos importantes ... encima les falta explicar los errores constantes que se presentan.
Vi que mucha gente se esta perdiendo en estas clases, primero, tranquilos, no se rindan, yo estoy igual que ustedes, recien arranco con la programación y algunos ejercicios o conceptos no me salen!
Asi que aca dejo mi aporte:
1)Repasar repasar y repasar
2)Vuelvan el video una y otra vez
Pd:Aca les dejo mi codigo, se que es una mala practica comentar todas las lineas pero quiza les pueda ayudar.
models.py:
import uuid
classClient: #Crea el objeto cliente
def __init__(self, name, company, email, position, uid=None): #Inicializa las instancias del objeto Cliente self.name= name
self.company= company
self.email= email
self.position= position
self.uid= uid or uuid.uuid4() #Si no recibe como parametro la variable "uid", devuelve el IDUUID4(), que se utiliza para generar ID'S en generral.defto_dict(self):returnvars(self) #Chequea lo que regresa el metodo __dict__ y nos realiza un diccionario de nuestro objeto
@staticmethod #Metodo estaticos dentro de nuestra clase, es un metodo que se puede ejecutar sin la necesidad de una instancia de clase
def schema(): #No hace falta pasar por parametro SELF porque el decorador @staticmethod nos permite hacerlo.return['name','company','email','position','uid'] #devuelve una lista de las variables que existen en el objeto CLIENT'
'''def dict(self): #Intentar el metodo to_dict con este metodo haber si funciona
returnvars(self)''```
services.py:
'''Logica de negocio,
#Aqui vamos a guardar todo lo que signifique cada funcion, update, list, create, etc'''
#Para no perderse aqui, es necesario que tengan bien claro los conceptos de la clase "Manejo de archivos con python"
def create_client(self, client):
with open(self.table_name, mode='a') as f: #Abre el archivo en modo "a = append", añade un cliente al archivo csv, cada vez que se crea uno
writer = csv.DictWriter(f, fieldnames=Client.schema()) #Referencia del WRITER
writer.writerow(client.to) #Escribir una nueva fila tabular dentro de nuestro archivo csv```
PD2:aclaro que recien soy principiante en esto y es por ahora mi forma de entender, me gustaria que alguien pueda corregirme si algo esta mal
Python no solo es un lenguaje orientado a objetos, sino un lenguaje formado por objetos.
Cuando creas una funcion en python con la palabra recervada "def" estas creando una variable del tipo "function" que ocupa un espacio de memoria determinado. Aunque esto suena tan trivial es muy importante para entender los decoradores.
El hecho de que esto sea así es que permite que los decoradores funcionen.
Veamos
from datetime import datetime, timedelta
def ultimo_dia_mes():'''
con esta funcion obtienes el ultimo dia del mes actual
:return: datetime
'''
hoy = datetime.now() mes =int(hoy.month) periodo =int(hoy.year)if mes ==12: mes =1 periodo +=1else: mes +=1returndatetime(year=periodo, month=mes, day=1)-timedelta(days=1)
si escribes el nombre de una definicion en la shell de python te regresara algo como esto
>>> ultimo_dia_mes
<function ultimo_dia_mes at 0x7fed01d60668>>>>
indicandote el tipo y la direccion de memoria de esa definicion
>>>type(ultimo_dia_mes)<type 'function'>>>>
Y como puedes ver si pides el tipo de esa definicion, python te dirá que es del tipo function. Por lo que las funciones son objetos.
Pero cuando le agregas los parentecis entonces estas llamando a un método llamado execute (no me consta en que en python 3 se llame igual) del objeto function que lo que hace es ejecutar de manera secuencial las lineas de codigo que agregaste a la definicion.
Con esto aclarado veamos los decoradores.
Los decoradores son funciones (objetos) que en su método execute (el metodo que manda a ejecutar las linea de codigo secuencial que mencioné antes) tiene como parametro otra funcion (otro objeto por cierto) esto con el objetivo de poder ejecutar código definido en el decorador y luego decidir si ejecutar el de la funcion original.
import math
# este será el decorador, veamos como se construye
def solo_numeros_positivos(funcion_matematica_original_aplicada_al): # funcion handler. # esta funcion debe tener la misma cantidad de parametros
# que las funciones donde se va a usar el decorador
def validar_positivo(numero):if not numero ==0: # si el numero no es cero, ejecuto la funcion original sobre el numero
returnfuncion_matematica_original_aplicada_al(numero)else: # pero sino regreso un error indicando que solo acepto numeros positivos
raise(BaseException("solo numeros positivos por favor"))
# ahora esta funcion que acabamos de definir dentro del decorador es la que regresamos
# pero sin los parentecis, ya que queremos regresar la definicion de la funcion
# y no ejecutarla
return validar_positivo
@solo_numeros_positivos
def mitad(numero):return numero /2
@solo_numeros_positivos
def logaritmo(numero):return math.log(numero)
ahora si ejecutas mitad o logaritmo y pones de parametro "0", entonces no se ejecutara el codigo dentro de mitad ni logaritmo sino que se ejecuatara el raise con el error de "solo numeros positivos por favor"