es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Agregar dinámicamente una propiedad a una clase con el fin de devolver un nuevo objeto en cada llamada.

Tengo una clase Car:

class Car:
    def __init__(self, speed):
        self.speed = speed

También tengo un diccionario cars que contiene nombres y velocidades de algunos coches:

cars = {"bmw" : 250, "ferrari": 350, ...}

Ahora quiero tener una clase contenedora que tenga una propiedad para cada uno de estos coches predefinidos, de modo que llamar a la propiedad cree un nuevo objeto Car cada vez; algo así:

class PredefinedCars:
   @property
   def bmw(self):
      return Car(250)

@property
   def ferrari(self):
      return Car(350)

...

Pero no quiero escribir una propiedad para cada coche en el diccionario, porque hay demasiados y además el diccionario puede cambiar. En su lugar, quiero añadir dinámicamente todas esas propiedades durante la instanciación.
He intentado esto:

class PredefinedCars:
def init(self, cars):
for name, speed in cars.items():
setattr(self, name, Car(speed))

Esto crea los objetos según lo previsto:

predef_cars = PredefinedCars(dic)
predef_cars.bmw.speed
250
predef_cars.ferrari.speed
350

Sin embargo, cada vez que llamo a predef_cars.bmw, por ejemplo, se devuelve el mismo objeto:

predef_cars.ferrari
<main.car at=”” 0x1145cba30=””>
predef_cars.ferrari
<main.car at=”” 0x1145cba30=””>

Además, los atributos pueden ser reasignados, lo cual no es lo que quiero:

predef_cars.ferrari = 0
predef_cars.ferrari
0

Otra solución que intenté fue esta:

class PredefinedCars:
def init(self, cars):
for name, speed in cars.items():
setattr(PredefinedCars, name, property(lambda self: Car(speed)))

Esto soluciona ambos problemas anteriores, es decir, ahora en cada llamada se devuelve un nuevo objeto:

predef_cars = PredefinedCars(dic)
predef_cars.ferrari
<main.car at=”” 0x1145e76a0=””>
predef_cars.ferrari
<main.car at=”” 0x1145ea3d0=””>

y la propiedad no puede ser reasignada:

predef_cars.ferrari = 1
AttributeError: can’t set attribute

Sin embargo, ahora hay un nuevo problema, y es que todos los coches están siendo instanciados con el último valor de velocidad:

predef_cars.ferrari.speed
350
predef_cars.bmw.speed # Debería devolver 250, pero devuelve también 350:
350
“`
¿Cómo puedo solucionar esto?</main.car></main.car></main.car></main.car>

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Puedes usar un cierre para capturar el valor de speed para cada coche durante la instanciación de PredefinedCars, y luego usar ese valor en la función lambda dentro del método property. Aquí tienes una implementación actualizada:

    class PredefinedCars:
        def __init__(self, cars):
            for name, speed in cars.items():
                prop = (lambda s: property(lambda self: Car(s)))(speed)
                setattr(self.__class__, name, prop)
    

    En esta implementación, creamos una función lambda que toma el valor de speed y devuelve un nuevo objeto property que crea un nuevo objeto Car con esa velocidad cada vez que se llama. Llamamos inmediatamente esta función lambda con el valor de speed para crear el objeto property, y luego lo establecemos como un atributo de la clase usando setattr. Ten en cuenta que usamos self.__class__ en lugar de PredefinedCars para establecer el atributo en el objeto de la clase en sí, en lugar de en las instancias de la clase.

    Con esta implementación, puedes crear un objeto PredefinedCars y obtener un nuevo objeto Car cada vez que accedes a una propiedad:

    predef_cars = PredefinedCars(cars)
    print(predef_cars.bmw.speed)  # Salida: 250
    print(predef_cars.ferrari.speed)  # Salida: 350
    print(predef_cars.ferrari.speed)  # Salida: 350 (objeto nuevo cada vez)
    

    Y no puedes reasignar las propiedades:

    predef_cars.ferrari = 0  # Lanza AttributeError: no se puede establecer el atributo
    

Comments are closed.