Python @property 语法
@property 是 Python 的一个内置装饰器,它是 Python 中用于创建属性的 “语法糖”。
@property 允许我们将类的方法转换为像属性一样访问的特殊方法,从而在不改变外部代码调用方式的前提下,对属性的获取(getter)、设置(setter)或删除(deleter)等操作执行特定的逻辑。
语法:
class MyClass:
def __init__(self, value):
self._value = value # 内部私有变量,按约定加下划线
@property
def my_attribute(self):
'''
Getter 方法,用于获取属性值。
此方法的文档字符串将成为属性的文档字符串。
'''
# 返回属性值
return self._value
@my_attribute.setter
def my_attribute(self, new_value):
'''
Setter 方法,用于设置属性值。
在设置值时可以执行验证或其他逻辑。
'''
# 设置属性值
self._value = new_value
@my_attribute.deleter
def my_attribute(self):
'''
Deleter 方法,用于删除属性。
在删除时可以执行清理逻辑。
'''
# 删除属性
del self._value说明:
@property 装饰器由以下 3 部分组成。
@property (Getter):直接放在一个方法上方,会将该方法转变为一个只读属性(getter)。当外部代码尝试读取(访问)这个属性时,就会调用被 @property 装饰的方法。@<attribute_name>.setter (Setter):紧随 @property 定义的 getter 之后,放在一个同名的方法上方。当外部代码尝试修改(赋值)这个属性时,就会调用被 @setter 装饰的方法。该方法会接收两个参数:实例本身(self)和要设置的新值。@<attribute_name>.deleter (Deleter):紧随 @property 定义的 getter 之后,放在一个同名的方法上方。当外部代码尝试使用 del 关键字删除这个属性时,就会调用被 @deleter 装饰的方法。该方法只接收一个参数:实例本身(self)。
提示:
- @property 装饰器是 property() 内置函数的一种 “语法糖” 形式,它让代码更简洁、更易读。
- 使用 @property 可以将类内部的私有(按约定)变量封装起来,通过公共属性来控制其访问、修改和删除,实现了数据保护和校验。
- 一个属性不一定需要所有 3 个方法(getter、setter、deleter)。例如,如果只定义 @property 而没有 @setter,则表示该属性是只读的。
Python @property 摘要
| 使用频率 | 高 |
|---|---|
| 官方文档 | 查看 |
| 相关函数 | @classmethod、@staticmethod |
Python @property 示例
接下来,我们通过几个简单的例子来讲解一下 Python @property 是如何使用的。
示例 1:@property 创建只读属性
class Circle:
def __init__(self, radius):
# _radius 是内部私有变量,按约定加下划线
# 通常不希望外部直接访问或修改它
self._radius = radius
@property
def radius(self):
# 圆的半径 (只读属性)
return self._radius
# 创建 Circle 对象
c = Circle(5)
# 访问只读属性
print(c.radius)
# 尝试修改只读属性
try:
c.radius = 10
except AttributeError as e:
print(e)运行结果如下:
5
property 'radius' of 'Circle' object has no setter分析:
在 Circle 类中,radius 属性只定义了 @property 装饰的 getter 方法。此时我们可以像访问普通属性一样 c.radius 来获取它的值。
但如果我们尝试来修改它的值,Python 就会抛出 AttributeError 异常,这是因为它没有定义相应的 setter 方法。
示例 2:@property 创建读写属性(数据校验)
class Student:
def __init__(self, name, score):
self._name = name # 私有属性
self._score = 0 # 初始设为 0,通过 setter 验证
# 调用 setter 来初始化 score,确保验证逻辑被执行
self.score = score
@property
def name(self):
# 学生的姓名
return self._name
@property
def score(self):
# 学生的成绩,读写属性,写入时进行验证
return self._score
@score.setter
def score(self, value):
if not isinstance(value, (int, float)):
raise TypeError('成绩必须是数字!')
if not (0 <= value <= 100):
raise ValueError('成绩必须在 0 到 100 之间!')
self._score = value
# 创建 Student 对象
s = Student('阿莫', 88)
print(f'姓名: {s.name}, 成绩: {s.score}')
# 修改成绩 (正常情况)
s.score = 95
print(f'姓名: {s.name}, 成绩: {s.score}')
# 尝试设置无效成绩 (触发校验错误)
try:
s.score = 105
except ValueError as e:
print(e)运行结果如下:
姓名: 阿莫, 成绩: 88
姓名: 阿莫, 成绩: 95
成绩必须在 0 到 100 之间!分析:
在这个例子中,score 属性通过 @property 定义了 getter,并通过 @score.setter 定义了 setter。
在 setter 中,我们添加了数据类型和范围的验证。这使得对 score 属性的任何赋值操作都会经过这些校验,从而确保了数据的有效性,从而增强了类的健壮性。同时,外部仍然可以像访问普通属性一样读写 score,而无需关心底层的校验逻辑。
示例 3:@property 实现计算属性
class Rectangle:
def __init__(self, width, height):
# 使用 setter 来初始化,确保校验逻辑被执行
self.width = width
self.height = height
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if value <= 0:
raise ValueError('宽度必须为正数!')
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
if value <= 0:
raise ValueError('高度必须为正数!')
self._height = value
@property
def area(self):
# 计算矩形的面积,这是一个计算属性,每次访问都会重新计算
return self._width * self._height
@property
def perimeter(self):
# 计算矩形的周长。这是一个计算属性,每次访问都会重新计算
return 2 * (self._width + self._height)
# 创建 Rectangle 对象
r = Rectangle(10, 5)
# 访问计算属性
print(f'矩形宽度: {r.width}, 高度: {r.height}')
print(f'矩形面积: {r.area}') # 每次访问都会重新计算
print(f'矩形周长: {r.perimeter}') # 每次访问都会重新计算
# 修改基础属性后,计算属性自动更新
r.width = 12
r.height = 6
print(f'矩形宽度: {r.width}, 高度: {r.height}')
print(f'矩形面积: {r.area}') # 面积自动更新
print(f'矩形周长: {r.perimeter}') # 周长自动更新运行结果如下:
矩形宽度: 10, 高度: 5
矩形面积: 50
矩形周长: 30
矩形宽度: 12, 高度: 6
矩形面积: 72
矩形周长: 36分析:
@property 可以用于创建一个计算属性。所谓的 “计算属性”,指的是该属性的值是根据其他属性动态计算出来的,而不是直接存储的。
在这个例子中,area 和 perimeter 都是计算属性。它们的值不是直接存储在对象中的,而是在每次访问时根据 _width 和 _height 动态计算出来的。这避免了数据冗余,并确保了数据的实时一致性。当我们修改 width 或 height 时,area 和 perimeter 会自动反映最新的计算结果。
