Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> python

Python @property decorador

Python @property decorador


Neste tutorial, você aprenderá sobre o decorador @property do Python; uma maneira Python de usar getters e setters na programação orientada a objetos.

A programação Python nos fornece um @property integrado decorador que torna o uso de getters e setters muito mais fácil na Programação Orientada a Objetos.

Antes de entrar em detalhes sobre o que @property decorador é, vamos primeiro construir uma intuição sobre por que ele seria necessário em primeiro lugar.


Classe sem getters e setters


Vamos supor que decidimos fazer uma classe que armazena a temperatura em graus Celsius. Também implementaria um método para converter a temperatura em graus Fahrenheit. Uma maneira de fazer isso é a seguinte:
class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

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

Podemos fazer objetos dessa classe e manipular o temperature atributo como desejamos:
# 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())

Saída
37
98.60000000000001

As casas decimais extras ao converter em Fahrenheit são devido ao erro aritmético de ponto flutuante. Para saber mais, visite Erro aritmético de ponto flutuante do Python.

Sempre que atribuirmos ou recuperarmos qualquer atributo de objeto como temperature como mostrado acima, o Python o pesquisa no __dict__ interno do objeto atributo de dicionário.
>>> human.__dict__
{'temperature': 37}

Portanto, man.temperature internamente se torna man.__dict__['temperature'] .

Usando getters e setters


Suponha que queremos estender a usabilidade do Celsius classe definida acima. Sabemos que a temperatura de qualquer objeto não pode chegar abaixo de -273,15 graus Celsius (Zero Absoluto em Termodinâmica)

Vamos atualizar nosso código para implementar essa restrição de valor.

Uma solução óbvia para a restrição acima será ocultar o atributo temperature (torne-o privado) e defina novos métodos getter e setter para manipulá-lo. Isso pode ser feito da seguinte forma:
# 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, o método acima introduz dois novos get_temperature() e set_temperature() métodos.

Além disso, temperature foi substituído por _temperature . Um sublinhado _ no início é usado para denotar variáveis ​​privadas em Python.


Agora, vamos usar esta implementação:
# 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())



Saída
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 atualização implementou com sucesso a nova restrição. Não podemos mais definir a temperatura abaixo de -273,15 graus Celsius.

Observação :As variáveis ​​privadas não existem realmente em Python. Há simplesmente normas a serem seguidas. A linguagem em si não aplica nenhuma restrição.
>>> human._temperature = -300
>>> human.get_temperature()
-300

No entanto, o maior problema com a atualização acima é que todos os programas que implementaram nossa classe anterior precisam modificar seu código de obj.temperature para obj.get_temperature() e todas as expressões como obj.temperature = val para obj.set_temperature(val) .

Essa refatoração pode causar problemas ao lidar com centenas de milhares de linhas de código.

Em suma, nossa nova atualização não era compatível com versões anteriores. É aqui que @property vem resgatar.

A classe da propriedade


Uma maneira Python de lidar com o problema acima é usar o property classe. Aqui está como podemos atualizar nosso 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)

Adicionamos um print() função dentro de get_temperature() e set_temperature() observar claramente que eles estão sendo executados.

A última linha do código cria um objeto de propriedade temperature . Simplificando, a propriedade anexa algum código (get_temperature e set_temperature ) aos acessos de atributo de membro (temperature ).

Vamos usar este código de atualização:
# 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

Saída
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, qualquer código que recupere o valor de temperature chamará automaticamente get_temperature() em vez de uma pesquisa de dicionário (__dict__). Da mesma forma, qualquer código que atribua um valor a temperature chamará automaticamente set_temperature() .

Podemos até ver acima que set_temperature() foi chamado mesmo quando criamos um objeto.
>>> human = Celsius(37)
Setting value...

Você consegue adivinhar por quê?

A razão é que quando um objeto é criado, o __init__() método é chamado. Este método tem a linha self.temperature = temperature . Esta expressão chama automaticamente set_temperature() .

Da mesma forma, qualquer acesso como c.temperature chama automaticamente get_temperature() . Isso é o que a propriedade faz. Aqui estão mais alguns exemplos.
>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value

>>> c.to_fahrenheit()
Getting value
98.60000000000001

Usando property , podemos ver que nenhuma modificação é necessária na implementação da restrição de valor. Assim, nossa implementação é compatível com versões anteriores.

Observação :O valor real da temperatura é armazenado no _temperature privado variável. O temperature attribute é um objeto de propriedade que fornece uma interface para essa variável privada.

O decorador @property


Em Python, property() é uma função interna que cria e retorna um property objeto. A sintaxe desta função é:
property(fget=None, fset=None, fdel=None, doc=None)

Onde,

Como visto na implementação, esses argumentos de função são opcionais. Assim, um objeto de propriedade pode simplesmente ser criado da seguinte maneira.
>>> property()
<property object at 0x0000000003239B38>

Um objeto de propriedade tem três métodos, getter() , setter() e deleter() para especificar fget , fset e fdel em um momento posterior. Isso significa que a linha:
temperature = property(get_temperature,set_temperature)

pode ser decomposto como:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Esses dois pedaços de códigos são equivalentes.

Programadores familiarizados com Python Decorators podem reconhecer que a construção acima pode ser implementada como decoradores.

Nem podemos definir os nomes get_temperature e set_temperature pois eles são desnecessários e poluem o namespace da classe.

Para isso, reutilizamos o temperature name ao definir nossas funções getter e setter. Vamos ver como implementar isso como um 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)

Saída
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

A implementação acima é simples e eficiente. É a maneira recomendada de usar property .

python

  1. Tipos de dados Python
  2. Operadores Python
  3. Instrução de passagem do Python
  4. Argumentos da função Python
  5. Dicionário Python
  6. Herança Python
  7. Sobrecarga do Operador Python
  8. Iteradores Python
  9. Fechamentos Python
  10. Python @property decorador