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

Python @property decorador

Python @decorador de propiedades

En este tutorial, aprenderá sobre el decorador Python @property; una forma pitónica de usar getters y setters en la programación orientada a objetos.

La programación de Python nos proporciona un @property incorporado decorador que hace que el uso de getter y setters sea mucho más fácil en la programación orientada a objetos.

Antes de entrar en detalles sobre qué @property decorador es, primero construyamos una intuición sobre por qué sería necesario en primer lugar.


Clase sin getters y setters

Supongamos que decidimos hacer una clase que almacene la temperatura en grados Celsius. También implementaría un método para convertir la temperatura en grados Fahrenheit. Una forma de hacerlo es la siguiente:

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

Podemos hacer objetos a partir de esta clase y manipular el temperature atributo como deseemos:

# Basic method of setting and getting attributes in Python
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32


# Create a new object
human = Celsius()

# Set the temperature
human.temperature = 37

# Get the temperature attribute
print(human.temperature)

# Get the to_fahrenheit method
print(human.to_fahrenheit())

Salida

37
98.60000000000001

Los lugares decimales adicionales al convertir a Fahrenheit se deben al error aritmético de punto flotante. Para obtener más información, visite Error aritmético de punto flotante de Python.

Cada vez que asignamos o recuperamos cualquier atributo de objeto como temperature como se muestra arriba, Python lo busca en el __dict__ integrado del objeto atributo de diccionario.

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

Por lo tanto, man.temperature internamente se convierte en man.__dict__['temperature'] .


Uso de captadores y definidores

Supongamos que queremos extender la usabilidad del Celsius clase definida anteriormente. Sabemos que la temperatura de cualquier objeto no puede bajar por debajo de -273,15 grados centígrados (cero absoluto en termodinámica)

Actualicemos nuestro código para implementar esta restricción de valor.

Una solución obvia a la restricción anterior será ocultar el atributo temperature (hágalo privado) y defina nuevos métodos getter y setter para manipularlo. Esto se puede hacer de la siguiente manera:

# Making Getters and Setter methods
class Celsius:
    def __init__(self, temperature=0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # getter method
    def get_temperature(self):
        return self._temperature

    # setter method
    def set_temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible.")
        self._temperature = value

Como podemos ver, el método anterior introduce dos nuevos get_temperature() y set_temperature() métodos.

Además, temperature fue reemplazado por _temperature . Un guión bajo _ al principio se usa para indicar variables privadas en Python.


Ahora, usemos esta implementación:

# Making Getters and Setter methods
class Celsius:
    def __init__(self, temperature=0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # getter method
    def get_temperature(self):
        return self._temperature

    # setter method
    def set_temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible.")
        self._temperature = value


# Create a new object, set_temperature() internally called by __init__
human = Celsius(37)

# Get the temperature attribute via a getter
print(human.get_temperature())

# Get the to_fahrenheit method, get_temperature() called by the method itself
print(human.to_fahrenheit())

# new constraint implementation
human.set_temperature(-300)

# Get the to_fahreheit method
print(human.to_fahrenheit())

Salida

37
98.60000000000001
Traceback (most recent call last):
  File "<string>", line 30, in <module>
  File "<string>", line 16, in set_temperature
ValueError: Temperature below -273.15 is not possible.

Esta actualización implementó con éxito la nueva restricción. Ya no se nos permite establecer la temperatura por debajo de -273,15 grados centígrados.

Nota :Las variables privadas en realidad no existen en Python. Simplemente hay normas a seguir. El idioma en sí no aplica ninguna restricción.

>>> human._temperature = -300
>>> human.get_temperature()
-300

Sin embargo, el mayor problema con la actualización anterior es que todos los programas que implementaron nuestra clase anterior tienen que modificar su código de obj.temperature a obj.get_temperature() y todas las expresiones como obj.temperature = val a obj.set_temperature(val) .

Esta refactorización puede causar problemas al tratar con cientos de miles de líneas de códigos.

Con todo, nuestra nueva actualización no era compatible con versiones anteriores. Aquí es donde @property viene a rescatar.


La clase de propiedad

Una forma pitónica de lidiar con el problema anterior es usar el property clase. Así es como podemos actualizar nuestro código:

# using property class
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("Getting value...")
        return self._temperature

    # setter
    def set_temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible")
        self._temperature = value

    # creating a property object
    temperature = property(get_temperature, set_temperature)

Agregamos un print() función dentro de get_temperature() y set_temperature() para observar claramente que están siendo ejecutados.

La última línea del código crea un objeto de propiedad temperature . En pocas palabras, la propiedad adjunta algún código (get_temperature y set_temperature ) a los accesos de atributo de miembro (temperature ).

Usemos este código de actualización:

# using property class
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("Getting value...")
        return self._temperature

    # setter
    def set_temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible")
        self._temperature = value

    # creating a property object
    temperature = property(get_temperature, set_temperature)


human = Celsius(37)

print(human.temperature)

print(human.to_fahrenheit())

human.temperature = -300

Salida

Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
Traceback (most recent call last):
  File "<string>", line 31, in <module>
  File "<string>", line 18, in set_temperature
ValueError: Temperature below -273 is not possible

Como podemos ver, cualquier código que recupera el valor de temperature llamará automáticamente al get_temperature() en lugar de una búsqueda de diccionario (__dict__). Del mismo modo, cualquier código que asigne un valor a temperature llamará automáticamente al set_temperature() .

Incluso podemos ver arriba que set_temperature() fue llamado incluso cuando creamos un objeto.

>>> human = Celsius(37)
Setting value...

¿Puedes adivinar por qué?

La razón es que cuando se crea un objeto, el __init__() se llama al método. Este método tiene la línea self.temperature = temperature . Esta expresión llama automáticamente a set_temperature() .

Del mismo modo, cualquier acceso como c.temperature automáticamente llama a get_temperature() . Esto es lo que hace la propiedad. Aquí hay algunos ejemplos más.

>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value

>>> c.to_fahrenheit()
Getting value
98.60000000000001

Usando property , podemos ver que no se requiere ninguna modificación en la implementación de la restricción de valor. Por lo tanto, nuestra implementación es compatible con versiones anteriores.

Nota :El valor de temperatura real se almacena en el privado _temperature variable. El temperature atributo es un objeto de propiedad que proporciona una interfaz a esta variable privada.


El @decorador de propiedades

En Python, property() es una función integrada que crea y devuelve un property objeto. La sintaxis de esta función es:

property(fget=None, fset=None, fdel=None, doc=None)

donde,

Como se ve en la implementación, estos argumentos de función son opcionales. Entonces, un objeto de propiedad puede crearse simplemente de la siguiente manera.

>>> property()
<property object at 0x0000000003239B38>

Un objeto de propiedad tiene tres métodos, getter() , setter() y deleter() para especificar fget , fset y fdel en un punto posterior. Esto significa, la línea:

temperature = property(get_temperature,set_temperature)

se puede desglosar como:

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Estos dos códigos son equivalentes.

Los programadores familiarizados con Python Decorators pueden reconocer que la construcción anterior se puede implementar como decoradores.

Incluso no podemos definir los nombres get_temperature y set_temperature ya que son innecesarios y contaminan el espacio de nombres de la clase.

Para ello reutilizamos el temperature name al definir nuestras funciones getter y setter. Veamos cómo implementar esto como decorador:

# Using @property decorator
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("Getting value...")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value


# create an object
human = Celsius(37)

print(human.temperature)

print(human.to_fahrenheit())

coldest_thing = Celsius(-300)

Salida

Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
Traceback (most recent call last):
  File "", line 29, in 
  File "", line 4, in __init__
  File "", line 18, in temperature
ValueError: Temperature below -273 is not possible

La implementación anterior es simple y eficiente. Es la forma recomendada de usar property .


python

  1. Tipos de datos de Python
  2. Operadores de Python
  3. Declaración de paso de Python
  4. Argumentos de la función de Python
  5. Diccionario de Python
  6. Herencia de Python
  7. Sobrecarga del operador de Python
  8. Iteradores de Python
  9. Cierres Python
  10. Python @property decorador
  11. Fecha y hora de Python