English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Python has a great concept called property, 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 happy 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("-273degrees is not possible") self._temperature = value
Podemos ver en lo que estamos viendo que get_temperature() y set_temperature() ya han definido nuevos métodos, además, se ha sustituido _temperature por temperature. El guión bajo (_) al inicio indica una variable privada en Python.
>>> c = Celsius(-277) Traceback (última llamada): ... ValueError: Temperature below -273 is not possible >>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10) >>> c.set_temperature(-300) Traceback (última llamada): ... ValueError: Temperature below -273 is not possible
Esta actualización ha implementado con éxito las nuevas restricciones. Ya no se nos permite establecer la temperatura por debajo de-273.
Por favor, tenga en cuenta que no existen variables privadas en Python. Simplemente siga algunas normas y el lenguaje en sí no tiene restricciones.
>>> c._temperature = -300 >>> c.get_temperature() -300
Pero no es un gran problema. El mayor problema de esta actualización es que todos los clientes que implementaron la clase anterior en el programa deben modificar su código de obj.temperature a obj.get_temperature() y cambiar todas las asignaciones (por ejemplo, obj.temperature = val se cambia a obj.set_temperature(val).
Esta refactorización podría causar problemas a los clientes con cientos de miles de líneas de código.
En resumen, nuestra nueva actualización no es compatible con versiones anteriores. Aquí es donde @property hace su trabajo.
El método que Python utiliza para manejar este problema es property. Podemos hacerlo de esta manera.
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("Valor obtenido") return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("Below zero"273degrees is not possible") print("Set value") self._temperature = value temperature = property(get_temperature, set_temperature)
Y una vez ejecutado, en el shell se emite el siguiente código.
>>> c = Celsius()
Agregamos la función print() en get temperature() y set temperature() para observar claramente su ejecución.
La última línea del código crea un objeto property temperature. En pocas palabras, la propiedad adjunta algunos códigos (get_temperature y set_temperature) al acceso de atributo miembro (temperature).
任何检索温度值的代码都将自动调用get_temperature()而不是字典(__dict__)查找。 同样,任何为温度分配值的代码都会自动调用set_temperature()。 这是Python中的一项很酷的功能。
我们可以在上面看到即使创建对象时也会调用set_temperature()。
你能猜出为什么吗?
原因是创建对象时,将调用__init__()方法。 此方法的行是self.temperature = temperature。 此分配自动称为set_temperature()。
>>> c.temperature Getting value 0
同样,任何访问如c.temperature都会自动调用get_temperature()。 这就是属性的作用。 这里还有一些实例。
>>> c.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
通过使用属性,我们可以看到,我们修改了类并实现了值约束,而无需更改客户端代码。因此,我们的实现是向后兼容的。
最后请注意,实际温度值存储在私有变量_temperature中。 temperature属性是一个属性对象,它提供了与此私有变量的接口。
在Python中,property()是一个内置函数,用于创建并返回属性对象。该函数的签名是
property(fget=None, fset=None, fdel=None, doc=None)
其中,fget为获取属性值的函数,fset为设置属性值的函数,fdel为删除属性的函数,doc为字符串(如注释)。从实现中可以看出,这些函数参数是可选的。因此,可以简单地按照以下方式创建属性对象。
>>> property() <property object at 0x0000000003239B38>
属性对象有三个方法,getter()、setter()和deleter(),用于稍后指定fget、fset和fdel。这意味着
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.
Familiar withDecorators in PythonProgrammers can recognize that the above construction can be implemented as a decorator.
We can go further without defining 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 can be done.
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"273degrees is not possible") 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.