English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Python has a great concept called attribute, which makes the life of object-oriented programmers easier.
Before defining and understanding what @property is, let's understand why it is needed first.
Suppose you decideCreate aA class to store temperature in Celsius. It will also implement a method to convert temperature to Fahrenheit. One method is as follows.
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
We can create objects from this class and manipulate the attribute temperature as needed. Try these in the Python shell.
>>> # Create a new object >>> man = Celsius() >>> # Set temperature >>> man.temperature = 37 >>> # Get temperature >>> man.temperature 37 >>> # Get Fahrenheit >>> man.to_fahrenheit() 98.60000000000001
When converting to Fahrenheit, the extra decimal places are due to floating-point arithmetic errors (when trying to1.1 + 2.2).
As shown above, every time we allocate or retrieve any object attribute (such astemperature) when Python searches for it in the __dict__ dictionary of the object.
>>> man.__dict__ {'temperature': 37}
Therefore, man.temperature internally becomes man.__dict__['temperature'].
Now, let's further assume that our course is very popular among customers, and they are starting to use it in their programs. They have made various allocations to the objects.
One day, a trusted customer came to us and suggested that the temperature should not be lower than-273Celsius (students in the field of thermodynamics might say it is actually-273.15Celsius), also known as absolute zero. He further requires us to implement this value constraint. As a company that pursues customer satisfaction, we are pleased to hear this suggestion and have released1.01Version (an upgrade of the existing class).
One obvious method to solve the above constraints is to hide the attribute temperature (set it as private) and define new getter and setter interfaces to operate on it. This can be done as follows.
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("-273degree is impossible") self._temperature = value
Nous pouvons voir ci-dessus que get_temperature() et set_temperature() ont été définis de nouvelles méthodes, en plus de remplacer temperature par _temperature. Un trait de soulignement (_) au début indique une variable privée en 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
Cette mise à jour a réussi à mettre en œuvre de nouvelles restrictions. Nous ne sommes plus autorisés à définir la température en dessous de-273.
Veuillez noter que les variables privées n'existent pas en Python. Il suffit de suivre certaines normes. Le langage lui-même n'a aucune restriction.
>>> c._temperature = -300 >>> c.get_temperature() -300
Mais ce n'est pas un grand problème. Le plus grand problème de cette mise à jour est que tous les clients qui ont implémenté la classe précédente doivent modifier leur code de obj.temperature à obj.get_temperature() et de toutes les affectations (par exemple obj.temperature = val modifié par obj.set_temperature(val)).
Cette refonte pourrait causer des problèmes à nos clients avec des centaines de milliers de lignes de code.
En résumé, notre nouvelle mise à jour n'est pas compatible avec les versions antérieures. C'est là que @property joue son rôle.
La méthode utilisée par Python pour traiter ce problème est property. Nous pouvons l'implémenter ainsi.
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("Valeur obtenue") return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("Below zero"273degree is impossible") print("Set value") self._temperature = value temperature = property(get_temperature, set_temperature)
Et une fois exécuté, dans le shell, émettez le code suivant.
>>> c = Celsius()
Nous avons ajouté la fonction print() dans get temperature() et set temperature() pour observer clairement leur exécution.
La dernière ligne du code crée un objet property nommé temperature. En bref, l'attribut adjoint certains codes (get_temperature et set_temperature) à l'accès aux attributs membres (temperature).
Tout code de récupération de la valeur de la température appelle automatiquement get_temperature() plutôt que de chercher dans le dictionnaire (__dict__). De même, tout code d'affectation de la valeur de la température appelle automatiquement set_temperature(). C'est une fonctionnalité très cool en Python.
Nous pouvons voir sur le dessus que set_temperature() est appelé même lors de la création de l'objet.
Pouvez-vous deviner pourquoi?
La raison en est que la méthode __init__() est appelée lors de la création de l'objet. La ligne de cette méthode est self.temperature = temperature. Cette affectation est automatiquement appelée set_temperature().
>>> c.temperature Obtenir une valeur 0
De même, toute accès comme c.temperature appelle automatiquement get_temperature(). Voici le rôle des attributs. Voici quelques exemples.
>>> c.temperature = 37 Définir une valeur >>> c.to_fahrenheit() Obtenir une valeur 98.60000000000001
En utilisant les attributs, nous pouvons voir que nous avons modifié la classe et réalisé la contrainte de valeur sans changer le code client. Par conséquent, notre implémentation est compatible avec le code existant.
Enfin, notez que la valeur réelle de la température est stockée dans la variable privée _temperature. L'attribut temperature est un objet attribut qui fournit une interface avec cette variable privée.
En Python, property() est une fonction intégrée, utilisée pour créer et renvoyer l'objet attribut. La signature de cette fonction est
property(fget=None, fset=None, fdel=None, doc=None)
Dans ce cas, fget est la fonction pour obtenir la valeur de l'attribut, fset est la fonction pour définir la valeur de l'attribut, fdel est la fonction pour supprimer l'attribut, doc est une chaîne de caractères (comme les commentaires). Il est possible de voir de l'implémentation que ces paramètres des fonctions sont optionnels. Par conséquent, il est possible de créer simplement l'objet attribut de la manière suivante.
>>> property() <property object at 0x0000000003239B38>
L'objet attribut possède trois méthodes, getter(), setter() et deleter(), qui seront spécifiées plus tard comme fget, fset et fdel. Cela signifie que
temperature = property(get_temperature, set_temperature)
can also be decomposed into
# Create an empty property temperature = property() # Set fget temperature = temperature.getter(get_temperature) # Set fset temperature = temperature.setter(set_temperature)
These two pieces of code are equivalent.
FamiliarDecorators in PythonProgrammers can recognize that the above constructions can be implemented as decorators.
We can go further and not define names get_temperature, set_temperature, as they are unnecessary and may affect the class namespace. To do this, we reuse the name temperature when defining getter and setter functions. This is possible.
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("Get value") return self._temperature @temperature.setter def temperature(self, value): if value < -273: raise ValueError("Below zero"273degree is impossible") print("Set value") self._temperature = value
The above implementation is a simple and recommended method for creating properties. When looking for properties in Python, you are likely to encounter these types of constructions.
Well, that's all for today.