Clases en Python al usar Append, creo que me tope con un Bug

136 views
Skip to first unread message

Antonio Meza

unread,
Mar 20, 2022, 6:21:54 PM3/20/22
to VFP a Python
Hola, estoy retomando el tema de Python, estoy usando FastApi para crear un Api Rest, el problema no esta en FastApi sino en Python puro al crear un clase y en ella agregar una Lista y llenarla con append


El problema que se me presenta es que al instanciar la clase 2 o mas veces el valor de la propiedad List se pasa a todas las instancias por lo que no se si es un bug, es algo normal, o algo estoy haciendo mal, sin embargo con los otros tipos de valores de las propiedades no sucede, solo pasa al usar específicamente Append para agregar valores.

Para que puedan reproducir el error les paso una simple clase que mas abajo se instancia 2 veces y al poner los valores en la primera resulta que en la propiedad List se pasa ese valor a la segunda instancia cuando esto no debería ser pues se supone que son 2 objetos diferentes.

class Saludo():
    mensaje: str = ""
    lista: list = []

usuario = Saludo()
usuario.mensaje = "Hola Usuario"
usuario.lista.append("Usuario Lista")

empresa = Saludo()

print( "Usuario Mensaje:",  usuario.mensaje )
print( "Usuario Lista:", usuario.lista )
print( "--- --- ---")
print( "Empresa Mensaje:", empresa.mensaje )
print( "Empresa Lista:", empresa.lista )


Al ejecutar el archivo de prueba me imprime:

(env) PS D:\python\fastApi\fusion> py prueba.py
Usuario Mensaje: Hola Usuario
Usuario Lista: ['Usuario Lista']
--- --- ---
Empresa Mensaje:
Empresa Lista: ['Usuario Lista']

Como empresa es una instancia de Saludo() debería estar vacío ya que no he ingresado valor, sin embargo me muestra el valor de usuario

Tengo 2 días con esto, para resolverlo de momento agregue a la clase el método __init__ para inicializar la propiedad list y funciona pero creo que algo raro esta pasando con Append

class Saludo():

    def __init__():
        lista: list = []
       
    mensaje: str = ""
    lista: list = []


saludos
Antonio Meza

Antonio Meza

unread,
Mar 20, 2022, 6:29:05 PM3/20/22
to VFP a Python
La clase quedo así realmente para evitar el bug o mi error de novato

class Saludo():

    def __init__( self ):
        self.lista: list = []

    mensaje: str = ""
    lista: list = []
usuario = Saludo()
usuario.mensaje = "Hola Usuario"
usuario.lista.append("Usuario Lista")

empresa = Saludo()

print( "Usuario Mensaje:",  usuario.mensaje )
print( "Usuario Lista:", usuario.lista )
print( "--- --- ---")
print( "Empresa Mensaje:", empresa.mensaje )
print( "Empresa Lista:", empresa.lista )

Al ejecutar el archivo de prueba me imprime correctamente:

(env) PS D:\python\fastApi\fusion> py prueba.py
Usuario Mensaje: Hola Usuario
Usuario Lista: ['Usuario Lista']
--- --- ---
Empresa Mensaje:
Empresa Lista: []

saludos
Antonio Meza

Carlos Miguel FARIAS

unread,
Mar 21, 2022, 6:04:54 AM3/21/22
to vfp-a-python
Quita la declaración de la lista de la clase, deja solo la del  __init__. Porque la lista como está forma parte de la clase, no de la instancia. 
Yo uso propiedades de instancia para ir cargando valores compartidos. 
Proba y contanos. 
Saludos: Miguel

--
Has recibido este mensaje porque estás suscrito al grupo "VFP a Python" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a vfp-a-python...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/vfp-a-python/13d021d9-e699-4ad3-b1a0-a73452f3455dn%40googlegroups.com.

Antonio Meza

unread,
Mar 21, 2022, 10:53:34 AM3/21/22
to VFP a Python
Muchas gracias Miguel, el detalle es que desconocía el termino atributo de clase e instancia de clase, ya vez que en VFP creas tu clase y la instancias varias veces y cada objeto no cambia los valores de los demás, pero en Python puedes compartir valores algo que no vi en los videos de Python sobre clases jejejeje

De causalidad no conoces un grupo de whatsapp Python porque estoy de lleno para hacer mi api rest con FastApi es mas sencillo que Flask, solo que el ORM SqlAlchemy me esta dando dolores de cabeza para las relaciones y en tu caso usas ORM o lo haces manual el acceso a datos, estoy usando PyMysql para conectarme a MariaDb. 


saludos

Carlos Miguel FARIAS

unread,
Mar 21, 2022, 1:10:40 PM3/21/22
to vfp-a-python
En mi caso uso, flask con postgresql, el manejador de datos lo hago con mi propia API, SQLAlquemy es demasiado grande, y posiblemente más lento, que lo que desarrollé (es mucho más simple, menos funcionalidad, lo soluciono componiendo sentencia SQL, 30 años con SQL al final lo estoy aprendiendo, 😁).
No conozco o estoy en grupo de whatsapp de Python (solo académicos y profesionales).
No uso FastApi.
Saludos: Miguel

Antonio Meza

unread,
Mar 21, 2022, 7:55:04 PM3/21/22
to VFP a Python
Muchas gracias !!

De casualidad sabes donde puedo ver ejemplo de como crear Clases o funciones que permitan hacer como los ORM, es que no se como es el termino correcto, pero que pueda hacer algo así:

select("*").from("clientes").all()

Es decir he visto ese tipo de programación en Python pero no se como buscar para poder hacer ese tipo de funciones.

Lo tengo separado porque no se como programar para que sea desde una línea.

sql.select("*")
sql.from("clientes")
sql.all()

saludos

Carlos Miguel FARIAS

unread,
Mar 22, 2022, 7:09:07 AM3/22/22
to vfp-a-python
En mi caso, utilizo un método donde paso los parámetros que necesito:
Por ejemplo, en mi clase persistencia, el select es así:

    def select(self, columnas: STR_ARRAY = None, filtros: STR_ARRAY = "",
               orden: STR_ARRAY = "", foraneas: STR_DICT = "",
               desde: int = 0, cantidad: int = None, distintas: bool = False,
               grupos: STR_ARRAY = "", teniendo: STR_ARRAY = "",
               modulo: str = INDEFINIDO, mostrar: bool = True) -> tuple:
        """Busca registros que cumplan una condición

Los parámetros columnas, filtros, orden, foráneos, grupos, cuando y teniendo,
    pueden ser cadenas. En esos casos, no se valida esa parte.
:param cantidad: cantidad a recuperar (todos si None, que es por omisión)
:param columnas: datos a retornar (todos si None)
    Puede ser str o ARRAY, si ARRAY puede ser ARRAY de str o de ARRAYs.
    Si None, asume '*' todos los campos.
    Si str: corresponde al sql de las columnas a devolver (con funciones, etc.)
        No se valida internamente.
    Si ARRAY: puede ser ARRAY de str (a)
        Si (a), es una lista de columnas [alias.]nombre
            (si múltiples tablas deben incluir el alias de la tabla respectiva)
            la cadena puede contener expresiones (no se validan)
:param desde: registro inicial en el recupero, mínimo 0 OFFSET
:param distintas: filas distintas (cláusula DISTINCT si True).
    Error si se indican funciones o agrupamientos de columnas.
:param filtros: condiciones de filtro (ver armar filtros para forma presentar)
:param foraneas: diccionario con descripción de tablas foráneas:
    {BASE: alias_tabla_base,
     alias_tabla_base: (persistencia, BASE, pk, None),
     alias_tabla_no_base: (persistencia, JOIN, pk, fk), <- tantas como requiera}
     Donde JOIN: LEFT, INNER, ... pk tendrá por omision alias tabla_base,
     si el join es con otra tabla, incorporará en origen alias correspondiente
:param grupos: nombres o
     números de columna para agrupar (incompatible con
    distintas. Campos de grupos no pueden ser funciones, las columnas
    solicitadas deben figurar en grupos
:param orden: lista nombres o números de columnas para ordenar
:param teniendo: condiciones de filtro para grupos
:param modulo: módulo desde se invoca el select.
:param mostrar: Si verdadero, convierte los datos en cadena, columna Estado
                es convertida de código a Texto explicado.
:return cantidad registros encontrados, tupla con datos o ...
        0: No encontró ninguno, -2: error en datos,
        -4: error motor persistencia, -5: error en foráneos,
        -6: datos mal estructurados, -7: error en filtro"""

Acá te paso la cabecera de la función. Uso Annotations, que permite verificar al escribir que el tipo de datos sea apropiado (tanto cuando usas la variable dentro del método, cuando pasas parámetros a las funciones/métodos o cuando manejas los retornos (return) de ellos.
Las annotaciones también se pueden usar como metadata dentro de los códigos, lo que permite por ejemplo usar decoradores genéricos para validar que los datos pasados sean correctos
Saludos: Miguel

Antonio Meza

unread,
Mar 22, 2022, 1:39:16 PM3/22/22
to VFP a Python
Excelente, le voy a dar una estudiada, muchas gracias por tu valioso tiempo!!!

saludos
Antonio Meza

Ricardo Ramos

unread,
Aug 19, 2023, 5:17:46 AM8/19/23
to VFP a Python
No es un bug, en clases de Python existen los atributos de clase y los atributos de instancia.
En tu ejemplo: 

class Saludo():
    mensaje: str = ""
    lista: list = []

Has definido atributos de clase y esos estan disponibles para todos los objetos derivados de la clase.
Esos atributos son comunes para todas las instancias. Para tener atributos de de instancia, que sean propios
de los objetos, debes hacerlo a a través de los métodos de la clase. El casp más simple se crear los atributos
de instancia es definirlos en el métooo __init__()

Saludos,
Ricardo Ramos

Reply all
Reply to author
Forward
0 new messages