English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Tutorial de Fundamentos do Python

Controle de Fluxo do Python

Função do Python

Tipos de Dados do Python

Operações de Arquivos do Python

Objetos e Classes do Python

Data e Hora do Python

Conhecimento Avançado do Python

Manual de Referência do Python

@property do Python

O Python tem uma grande ideia chamada propriedade, que torna a vida do programador orientado a objetos mais simples.

Antes de definir e entender completamente o que é @property, vamos entender por que precisamos usá-lo primeiro.

um exemplo de instância

Suponha que você decidaCriar umClasse para armazenar temperatura em graus Celsius. Ela também implementará um método para converter temperatura para Fahrenheit. Uma dessas maneiras é a seguinte.

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

Podemos criar objetos a partir dessa classe e manipular o atributo temperature conforme necessário. Experimente isso no shell do Python.

>>> # Criar novo objeto
>>> man = Celsius()
>>> # Definir temperatura
>>> man.temperature = 37
>>> # Obter temperatura
>>> man.temperature
37
>>> # Obter temperatura em Fahrenheit
>>> man.to_fahrenheit()
98.60000000000001

Quando convertimos para Fahrenheit, os dígitos decimais extras são devido a erros de aritmética de ponto flutuante (ao tentar1.1 + 2.2).

Como mostrado acima, sempre que atribuímos ou recuperamos qualquer atributo de um objeto (comotemperature) O Python sempre realiza uma pesquisa no dicionário __dict__ do objeto quando chamado (man.get_temperature()).

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

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

Agora, vamos fazer uma suposição adicional de que nossos cursos são muito populares entre os clientes e eles começaram a usá-los em seus programas. Eles fizeram várias alocações de objetos.

Um dia, um cliente confiável veio até nós, sugerindo que a temperatura não deve ser inferior a-273Celsius (estudantes de engenharia térmica podem dizer que na verdade é)-273.15Celsius, também conhecido como zero absoluto. Ele também nos pediu para implementar essa restrição de valor. Como uma empresa que busca a satisfação do cliente, estamos felizes em ouvir essa sugestão e lançamos1.01Versão (atualização de classes existentes).

Uso de getter e setter

A uma solução óbvia para essa restrição é ocultar o atributo temperature (definindo-o como privado) e definir novos métodos getter e setter para operá-lo. Isso pode ser feito da seguinte forma.

class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)
    def to_fahrenheit(self):
        return(self.get_temperature()) * 1.8) + 32
    # new update
    def get_temperature(self):
        return self._temperature
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("-273graus não é possível")
        self._temperature = value

Podemos ver acima que get_temperature() e set_temperature() já definiram novos métodos, além de substituir _temperature por temperature. O sublinhado (_) no início indica variável privada no Python.

>>> c = Celsius(-277)
Traceback (last call most recent):
...
ValueError: Temperature below -273 is not possible
>>> c = Celsius(37)
>>> c.get_temperature()
37
>>> c.set_temperature(10)
>>> c.set_temperature(-300)
Traceback (last call most recent):
...
ValueError: Temperature below -273 is not possible

Essa atualização implementou com sucesso novas restrições. Não estamos mais permitidos de definir a temperatura abaixo-273.

Observe que as variáveis privadas não existem no Python. Basta seguir algumas normas. O próprio idioma não tem nenhuma restrição.

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

Mas isso não é um grande problema. O maior problema dessa atualização é que todos os clientes que implementaram a classe anterior na programação devem modificar seu código de obj.temperature para obj.get_temperature() e todas as atribuições (por exemplo, obj.temperature = val modificado para obj.set_temperature(val)).

Essa refatoração trará problemas para os clientes com dezenas de milhares de linhas de código.

Em resumo, nossa nova atualização não é compatível com versões anteriores. É aqui que o @property entra em ação.

O poder de @property

O método que o Python usa para lidar com esse problema é property. Podemos implementá-lo dessa forma.

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
    def get_temperature(self):
        print("O valor obtido")
        return self._temperature
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Abaixo de zero273graus não é possível")
        print("Valor definido")
        self._temperature = value
    temperature = property(get_temperature, set_temperature)

E, uma vez executado, em shell, emitir o seguinte código.

>>> c = Celsius()

Adicionamos a função print() em get temperature() e set temperature() para observar claramente sua execução.

A última linha do código cria um objeto property temperature. Em resumo, a propriedade adiciona alguns códigos (get_temperature e set_temperature) ao acesso de atributo membro (temperature).

Qualquer código que recupera o valor da temperatura automaticamente chama get_temperature() em vez de buscar no dicionário (__dict__). Da mesma forma, qualquer código que atribui um valor à temperatura automaticamente chama set_temperature(). Isso é uma funcionalidade muito legal no Python.

Podemos ver acima que, mesmo ao criar o objeto, é chamado set_temperature().

Você consegue adivinhar por quê?

A razão para isso é que, ao criar o objeto, é chamado o método __init__(). A linha dessa método é self.temperature = temperature. Esta atribuição é automaticamente chamada set_temperature().

>>> c.temperature
Obtendo valor
0

Da mesma forma, qualquer acesso como c.temperature automaticamente chama get_temperature(). Isso é a função da propriedade. Aqui há alguns exemplos.

>>> c.temperature = 37
Definindo valor
>>> c.to_fahrenheit()
Obtendo valor
98.60000000000001

Usando a propriedade, podemos ver que modificamos a classe e implementamos restrições de valor sem alterar o código do cliente. Portanto, nossa implementação é compatível com versões anteriores.

Por fim, note que o valor real da temperatura é armazenado na variável privada _temperature. A propriedade temperature é um objeto de propriedade que fornece uma interface para essa variável privada.

Aprofunde-se em property

No Python, property() é uma função integrada usada para criar e retornar o objeto de propriedade. A assinatura dessa função é

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

Dentro disso, fget é a função para obter o valor da propriedade, fset é a função para definir o valor da propriedade, fdel é a função para deletar a propriedade, doc é uma string (como um comentário). Pode-se ver da implementação que os parâmetros dessas funções são opcionais. Portanto, pode-se criar simplesmente o objeto de propriedade da seguinte forma.

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

O objeto de propriedade possui três métodos, getter(), setter() e deleter(), usados para especificar fget, fset e fdel posteriormente. Isso significa que

temperature = property(get_temperature, set_temperature)

também pode ser decomposta

# Criar atributo vazio
temperature = property()
# Definir fget
temperature = temperature.getter(get_temperature)
# Definir fset
temperature = temperature.setter(set_temperature)

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

FamiliarDecoradores no PythonProgramadores podem reconhecer que essa construção pode ser implementada como decorador.

Podemos ir ainda mais longe, não definindo os nomes get_temperature, set_temperature, pois eles são desnecessários e afetam o espaço de nomes da classe. Para isso, reusamos o nome temperature ao definir as funções getter e setter. Isso é possível.

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("Valor obtido")
        return self._temperature
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Abaixo de zero273graus não é possível")
        print("Valor definido")
        self._temperature = value

A implementação acima é um método simples e recomendado para criar atributos. Quando você procurar atributos em Python, você provavelmente encontrará esses tipos de construtores.

Bom, hoje é isso.