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