Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> python

Dominando el Decorador @property de Python: Getters y Setters Limpios y Compatibles

Dominando el Decorador @property de Python

Descubre cómo el decorador integrado @property de Python te permite escribir getters y setters concisos y mantenibles manteniendo tu API compatible hacia atrás.

El decorador @property de Python es una herramienta poderosa para encapsular acceso a atributos. Permite definir lógica personalizada que se ejecuta cada vez que un atributo se lee o escribe, sin forzar a los llamadores a cambiar su código. En las siguientes secciones recorreremos un ejemplo real —manejo de conversiones de temperatura—, mostraremos cómo imponer restricciones y explicaremos por qué @property es la solución idiomática.


Una Clase Sin Getters y Setters

Supongamos que necesitamos una clase simple que almacene temperatura en Celsius y pueda convertir a Fahrenheit:

class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

Usarlo es directo:

# Crear un nuevo objeto
human = Celsius()
# Establecer la temperatura
human.temperature = 37
# Leer la temperatura
print(human.temperature)
# Convertir a Fahrenheit
print(human.to_fahrenheit())

Salida

37 98.60000000000001

El resultado de punto flotante es esperado debido a la representación binaria de decimales. Al inspeccionar human.__dict__, verás que Python almacena el valor en una entrada de diccionario plana:

>>> human.__dict__
{'temperature': 37}

Añadiendo Restricciones con Getters y Setters

Objetos del mundo real a menudo necesitan validación. Por ejemplo, temperaturas no pueden caer bajo cero absoluto (-273.15 °C). Una forma directa es ocultar el atributo y exponer métodos getter/setter explícitos:

class Celsius:
    def __init__(self, temperature=0):
        self.set_temperature(temperature)
    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32
    # getter
    def get_temperature(self):
        return self._temperature
    # setter
    def set_temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperatura por debajo de -273.15 no es posible.")
        self._temperature = value

Ahora la clase impone el límite inferior, pero cualquier código existente que accediera a obj.temperature debe actualizarse para llamar obj.get_temperature() y obj.set_temperature(val). Eso es una refactorización mayor para bases de código grandes.

Además, las variables "privadas" de Python son solo convención de nombres —_temperature aún puede accederse directamente, rompiendo encapsulación. Aquí es donde @property brilla.


Usando la Clase property

Python ofrece una clase property que une funciones getter y setter a un nombre de atributo:

class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
    # getter
    def get_temperature(self):
        print("Obteniendo valor...")
        return self._temperature
    # setter
    def set_temperature(self, value):
        print("Estableciendo valor...")
        if value < -273.15:
            raise ValueError("Temperatura por debajo de -273.15 no es posible")
        self._temperature = value
    # exponer como propiedad
    temperature = property(get_temperature, set_temperature)

Con esta configuración, el siguiente código se comporta exactamente como la clase simple original, pero con validación tras bambalinas:

human = Celsius(37)
print(human.temperature)  # dispara getter
print(human.to_fahrenheit())  # usa getter internamente
human.temperature = -300  # dispara setter y lanza ValueError

Salida

Estableciendo valor... Obteniendo valor... 37 Obteniendo valor... 98.60000000000001 Estableciendo valor... Traceback (most recent call last): ... ValueError: Temperatura por debajo de -273 no es posible

Observa cómo el código que accede a temperature permanece inalterado; solo la implementación subyacente ganó validación.

Consejo: Los datos reales viven en _temperature; temperature es solo una interfaz conveniente y segura.


El Decorador @property

Mientras la clase property funciona, la sintaxis de decorador de Python ofrece una forma más limpia y legible de lograr el mismo resultado. El decorador crea automáticamente el objeto property y adjunta el getter y setter al nombre del atributo.

class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
    @property
    def temperature(self):
        print("Obteniendo valor...")
        return self._temperature
    @temperature.setter
    def temperature(self, value):
        print("Estableciendo valor...")
        if value < -273.15:
            raise ValueError("Temperatura por debajo de -273 no es posible")
        self._temperature = value

El uso permanece igual:

human = Celsius(37)
print(human.temperature)
print(human.to_fahrenheit())
# Intentar establecer temperatura inválida
try:
    Celsius(-300)
except ValueError as e:
    print(e)

Salida

Estableciendo valor... Obteniendo valor... 37 Obteniendo valor... 98.60000000000001 Estableciendo valor... Traceback (most recent call last): ... ValueError: Temperatura por debajo de -273 no es posible

Usar @property mantiene el espacio de nombres limpio, elimina la necesidad de nombres separados de métodos getter/setter, y es el patrón recomendado para la mayoría de desarrolladores Python.

python

  1. Dominando tipos de datos en Python: Guía práctica
  2. Dominando operadores en Python: Guía completa
  3. Dominando la sentencia pass en Python: Guía práctica
  4. Dominando argumentos de funciones en Python: Posicionales, por palabra clave y por defecto
  5. Dominando Diccionarios en Python: Creación, Manipulación y Técnicas Avanzadas
  6. Dominando la Herencia en Python: Conceptos, Sintaxis y Ejemplos Prácticos
  7. Dominando la Sobrecarga de Operadores en Python: Guía Práctica
  8. Dominando Iteradores en Python: Cómo Funcionan y Cómo Crear los Tuyos
  9. Explicación de Closures en Python: Cómo las Funciones Anidadas Capturan Variables
  10. Dominando el Decorador @property de Python: Getters y Setters Limpios y Compatibles
  11. Dominando datetime en Python: Guía Práctica de Fechas, Horas y Zonas Horarias