Resumen

Los decoradores @property en Python convierten métodos en atributos accesibles con la anotación de punto, logrando interfaces limpias, validación y cálculo automático detrás de escena. Con un getter convertido en propiedad y un setter controlado, tu clase gana encapsulación efectiva sin cambiar cómo lees o escribes valores.

¿Qué es property y por qué hace tu código más pythonico?

Usar property permite exponer datos como atributos, pero con la lógica de métodos. Así, el acceso es natural y no requiere paréntesis, mientras mantienes el control interno.

  • Convierte un método en atributo de solo lectura con @property.
  • Controla la escritura con @nombre.setter y valida entradas.
  • Accede con la anotación de punto, sin paréntesis.

¿Cómo convertir un getter en propiedad con @property?

Primero, elimina el método get_... y declara la propiedad. Luego úsala como un atributo.

class Libro:
    def __init__(self, titulo, autor, ISBN):
        self.titulo = titulo
        self.autor = autor
        self.ISBN = ISBN
        self._veces_prestado = 0  # parámetro interno

    @property
    def veces_prestado(self):
        return self._veces_prestado

En el flujo de impresión, en lugar de retornar solo el título desde libros_disponibles, retorna el objeto libro y accede así:

# en main
for libro in libros_disponibles():
    print(libro.titulo, libro.veces_prestado)  # sin paréntesis

¿Cómo controlar la escritura con setter y validación?

Con el setter defines cómo se asigna y validas valores. Si no cumple, genera un error claro.

class Libro:
    # ...
    @veces_prestado.setter
    def veces_prestado(self, valor):
        if valor > 0:
            self._veces_prestado = valor
        else:
            raise ValueError("El valor de veces prestado debe ser mayor a cero")

Prueba en main asignando un valor negativo y ejecuta:

python main.py

Verás un ValueError con el mensaje de validación. Esto ayuda a detectar un bug antes de que llegue a usuarios reales.

¿Cómo usar properties para computar y mostrar datos?

Además de exponer valores, puedes crear propiedades que computan resultados a partir del estado interno. Por ejemplo, una propiedad es_popular basada en datos internos se accede como atributo y no necesita setter.

  • Propiedades computadas como es_popular evitan estados inconsistentes.
  • No declaras setter si no quieres permitir escritura.

Para mostrar información compuesta, crea una propiedad que devuelva una cadena con título, autor e ISBN.

class Libro:
    # ...
    @property
    def descripcion_completa(self):
        return f"{self.titulo} por {self.autor} {self.ISBN}"

Úsala en la salida en main con la anotación de punto:

print(libro.descripcion_completa)

¿Cómo se integra en main y biblioteca?

La integración es directa si retornas objetos libro en lugar de cadenas.

  • Modifica libros_disponibles para retornar la lista de objetos Libro.
  • Itera y accede a libro.titulo, libro.veces_prestado y libro.descripcion_completa.
  • Evita paréntesis: las propiedades se leen como atributos.
  • Ejecuta con python main.py para validar resultados.

¿Qué reto práctico puedes implementar ahora?

Pon en práctica las propiedades para reforzar encapsulación y claridad.

  • Agrega una propiedad nombre completo a la clase usuario que combine nombre y cédula.
  • Crea una propiedad en biblioteca para libros disponibles que retorne la lista de todos los libros disponibles.
  • Comparte tu implementación y comenta cómo validaste las asignaciones.

¿Tienes otra idea de propiedad útil en tu modelo de dominio? Cuéntala en los comentarios y qué validaciones agregarías.