主要内容是翻译自:https://www.programiz.com/python-programming/property
目录
Python编程为我们提供了一个内置的@property
装饰器,该装饰器使面向对象编程中的getter和setter的使用更加容易。
回顾一下,什么是装饰器:【Python】装饰器
为什么需要@property装饰器
没有getter和setter的类
建立一个以摄氏度为单位存储温度的类。并实现一种将温度转换为华氏度的方法。一种方法如下:
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
创建对象,并调用temperature
属性:
# 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) # 37 # 类属性的使用: 类名.属性名
print(human.__dict__) # {'temperature': 37}
# Get the to_fahrenheit method
print(human.to_fahrenheit()) # 98.60000000000001
使用getter和setter
更正确的类声明:隐藏属性temperature
(将其设为私有),并定义新的getter和setter方法来对其进行操作。
# Making Getters and Setter methods
class Celsius:
def __init__(self, temperature=0):
self.set_temperature(temperature)
# setter method
def set_temperature(self, value):
if value < -273.15:
raise ValueError("Temperature below -273.15 is not possible.")
self._temperature = value # 类变量,在Celsius类中共享
# getter method
def get_temperature(self):
return self._temperature # 私有变量
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
if __name__ == '__main__':
# Create a new object, set_temperature() internally called by __init__
human = Celsius(37)
# Get the temperature attribute via a getter
print(human.get_temperature()) # 37 # 现在类变量的使用从.转变为调用get方法
# Get the to_fahrenheit method, get_temperature() called by the method itself
print(human.to_fahrenheit()) # 98.60000000000001
# new constraint implementation
human.set_temperature(-300)
# Get the to_fahreheit method
print(human.to_fahrenheit())
"""
Traceback (most recent call last):
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property3.py", line 31, in <module>
human.set_temperature(-300)
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property3.py", line 9, in set_temperature
raise ValueError("Temperature below -273.15 is not possible.")
ValueError: Temperature below -273.15 is not possible.
"""
However, the bigger problem with the above update is that all the programs that implemented our previous class have to modify their code from
obj.temperature
toobj.get_temperature()
and all expressions likeobj.temperature = val
toobj.set_temperature(val)
.简单来说,我们原来使用类变量只需要 类.属性名 就可以访问了,但是现在需要 类.get方法() 来访问变量。
The property Class
We added a print()
function inside get_temperature()
and set_temperature()
to clearly observe that they are being executed.
The last line of the code makes a property object temperature
. Simply put, property attaches some code (get_temperature
and set_temperature
) to the member attribute accesses (temperature
).
说明以下代码:
当创建对象时,__init__()
将调用该方法。此方法已行self.temperature = temperature
。此表达式自动调用set_temperature()
。
同样,任何访问,例如c.temperature
自动调用get_temperature()
。这就是property的作用。
# 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)
"""
a property object temperature. Simply put,
property attaches some code (get_temperature and set_temperature) to the member attribute accesses (temperature)
property将 get_temperature 和 set_temperature 方法,附加到temperature的类变量中
"""
if __name__ == '__main__':
human = Celsius(37)
print('******************************')
print(human.temperature)
# print(human.__dict__) # {'_temperature': 37}
print(human.to_fahrenheit()) # 调用方法: 类.方法
print('------------------------------')
human.temperature = -300
"""
Setting value...
******************************
Getting value...
37
Getting value...
98.60000000000001
------------------------------
Setting value...
Traceback (most recent call last):
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property4.py", line 37, in <module>
human.temperature = -300
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property4.py", line 18, in set_temperature
raise ValueError("Temperature below -273.15 is not possible")
ValueError: Temperature below -273.15 is not possible
Process finished with exit code 1
"""
实际温度值存储在私有_temperature
变量中。该temperature
属性是为该私有变量提供接口的属性对象。
@property装饰器
在Python中,property()
是一个内置函数,用于创建并返回一个property
对象。该函数的语法为:
property(fget=None, fset=None, fdel=None, doc=None)
fget
是获取属性值的函数fset
是设置属性值的功能fdel
是删除属性的功能doc
是一个字符串(如评论)
从实现中可以看出,这些函数参数是可选的。因此,可以简单地如下创建属性对象。
>>> property()
<property object at 0x0000000003239B38>
Property对象有三种方法,getter()
,setter()
,和deleter()
指定fget
,fset
并fdel
在稍后的点。这意味着,该行:
temperature = property(get_temperature,set_temperature)
可以细分为:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
这两段代码是等效的。
熟悉Python Decorators的程序员可以认识到上述构造可以实现为装饰器。
我们甚至无法定义名称get_temperature
,set_temperature
由于它们是不必要的,因此会污染类名称空间。
为此,我们temperature
在定义getter和setter函数的同时重用了名称。让我们看一下如何将其实现为装饰器:
# property()是一个内置函数,用于创建并返回一个property对象。该函数的语法为:
# property(fget=None, fset=None, fdel=None, doc=None)
"""
fget 是获取属性值的函数
fset 是设置属性值的功能
fdel 是删除属性的功能
doc 是一个字符串(如评论)
"""
# Property对象有三种方法,getter(),setter(),和deleter()指定fget,fset并fdel在稍后的点。这意味着,该行:
"""
temperature = property(get_temperature,set_temperature)
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
"""
# 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
if __name__ == '__main__':
# create an object
human = Celsius(37)
print('-'*10)
print(human.temperature)
print('-' * 10)
print(human.to_fahrenheit())
print('-' * 10)
coldest_thing = Celsius(-300)
"""
Setting value...
----------
Getting value...
37
----------
Getting value...
98.60000000000001
----------
Setting value...
Traceback (most recent call last):
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property5.py", line 54, in <module>
coldest_thing = Celsius(-300)
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property5.py", line 29, in __init__
self.temperature = temperature
File "F:/weiwenjing5/PycharmProjects/studyPython/study_property5.py", line 43, in temperature
raise ValueError("Temperature below -273 is not possible")
ValueError: Temperature below -273 is not possible
"""