Lección 30 de 45 · Módulos y orientación a objetos

Métodos y properties

Haz que los objetos no solo guarden estado: dales operaciones que lo modifican y propiedades que exponen información derivada con una interfaz simple.

ComportamientoEstado / acción / derivado@property
Índice · Lección 30/45
Un objeto resulta útil cuando protege las operaciones que tienen sentido para sus datos

En la lección anterior un objeto solo almacenaba atributos. Ahora vamos a colocar junto a esos datos las acciones que les pertenecen.

Un método expresa una acción. Una property permite consultar información como un atributo aunque se calcule mediante código.

Evidencia de aprendizaje

Clasificar algo como estado, acción o dato derivado y elegir método o property según esa función.

Un método recibe la instancia y puede cambiar su estado

El producto sabe reducir su propio stock
class Producto:
    def __init__(self, nombre, stock):
        self.nombre = nombre
        self.stock = stock

    def vender(self, unidades):
        self.stock -= unidades

producto = Producto("Teclado", 10)
producto.vender(3)
print(producto.stock)

vender() pertenece a Producto porque modifica una regla relacionada con un producto concreto. Al llamar producto.vender(3), self es ese objeto.

Un buen método evita repartir la misma regla por todo el programa

Sin vender(), distintos lugares podrían modificar stock cada uno a su manera. Al concentrar la operación en un método, sabes dónde cambiar la regla si mañana decides impedir ventas con stock insuficiente.

Pregunta de diseño sencilla: «¿esta acción depende de los datos de una instancia y tiene sentido pedirle al propio objeto que la realice?». Si sí, probablemente encaja como método.

Una property parece un atributo, pero puede calcular el valor al pedirlo

Precio con IVA calculado, no almacenado dos veces
class Producto:
    def __init__(self, precio):
        self.precio = precio

    @property
    def precio_con_iva(self):
        return self.precio * 1.21

producto = Producto(100)
print(producto.precio_con_iva)

Observa la llamada: no escribes paréntesis en precio_con_iva. Desde fuera se consulta como un atributo.

Esto evita guardar dos datos que podrían desincronizarse. Si cambia precio, la property calcula el valor actualizado la próxima vez que se consulta.

No uses @property solo para evitar paréntesis

Una property funciona bien para una consulta que conceptualmente parece un dato del objeto y es rápida de calcular. Una operación pesada, con efectos secundarios o que claramente representa una acción suele ser mejor como método.

producto.precio_con_iva suena a información. producto.enviar_factura() suena a acción. Esa diferencia lingüística es una buena brújula.

Clasifica antes de elegir sintaxis

Estado

stock: dato que la instancia guarda.

Acción

vender(3): cambia el estado → método.

Dato derivado

disponible: se calcula desde stock → puede ser property.

Parte A · método que cambia estado

Completa el método para restar las unidades vendidas al stock de la instancia.

Salida esperada
7
metodo.py
La salida aparecerá aquí.

Necesito una pista
  • El nuevo stock depende del stock actual y de unidades.
Ver una solución razonada
class Producto:
    def __init__(self, stock):
        self.stock = stock

    def vender(self, unidades):
        self.stock = self.stock - unidades

producto = Producto(10)
producto.vender(3)
print(producto.stock)

Parte B · property para un dato derivado

Añade una property disponible que calcule si queda stock. No guardes un segundo atributo que pueda desincronizarse.

Salida esperada
True
property.py
La salida aparecerá aquí.

Necesito una pista
  • Escribe @property sobre un método sin parámetros adicionales.
  • Devuelve self.stock > 0.
Ver una solución razonada
class Producto:
    def __init__(self, stock):
        self.stock = stock

    @property
    def disponible(self):
        return self.stock > 0

producto = Producto(2)
print(producto.disponible)

Una property no debería esconder una sorpresa

Si al leer usuario.nombre_completo la aplicación realiza una petición de red, modifica un archivo o tarda varios segundos, la interfaz resulta engañosa: parece una consulta simple pero provoca una acción costosa.

Reserva las properties para información que se sienta natural al «leer» el objeto.

Recupera lo anterior · minuto 6

Sin mirar apuntes

Un proyecto importa una clase Producto desde otro módulo. Cada producto guarda stock y ofrece una acción vender(). Señala origen del nombre, estado y acción.

Comprobar

El import indica de qué módulo viene Producto; stock es estado de cada instancia; vender() es un método que modifica ese estado.

Cierra este bloque distinguiendo datos, acciones y datos calculados

  • self.stock es estado almacenado en la instancia.
  • vender() es una acción que puede modificar ese estado.
  • precio_con_iva puede ser una property si se deriva de otros atributos.
Una buena señal de comprensión

Si puedes decidir razonadamente si algo debería ser atributo, método o property, ya tienes una base mucho más útil que memorizar decoradores.