Python property() 语法
property() 是 Python 的一个内置函数,它用于在类的定义中添加一个属性。
语法:
property(fget=None, fset=None, fdel=None, doc=None)说明:
property() 函数接收以下 4 个参数。
fget(可选,默认值:None):是用于获取属性值的函数。fset(可选,默认值:None):是用于设置属性值的函数。fdel(可选,默认值:None):是用于删除属性的函数。doc(可选):是属性文档字符串。
property() 在类内部使用,它返回一个属性对象,然后可以赋值给对象实例的属性。
提示:
- property() 最常见的用法是作为装饰器(@property、@<attribute>.setter、@<attribute>.deleter),这种方式更加简洁和推荐。
- 使用 property() 可以让你在不破坏现有外部代码的情况下,将公共属性转换为具有 getter / setter 逻辑的 “受控” 属性,这是 Python 优雅的封装机制。
Python property() 摘要
| 使用频率 | 中 |
|---|---|
| 官方文档 | 查看 |
| 相关函数 | hasattr()、getattr()、setattr()、delattr() |
Python property() 示例
接下来,我们通过几个简单的例子来讲解一下 Python property() 函数是如何使用的。
示例 1:property() 基本用法(函数式语法)
class Person:
def __init__(self, name):
self._name = name # 内部存储属性
def get_name(self):
print('获取名字')
return self._name
def set_name(self, value):
if not isinstance(value, str):
raise ValueError('名字必须是字符串')
print('设置名字')
self._name = value
def del_name(self):
print('删除名字')
del self._name
# 创建属性对象并绑定方法
name = property(get_name, set_name, del_name, '姓名属性')
p = Person('Jack')
print(p.name) # 输出:获取名字 → Jack
p.name = 'Lucy' # 输出:设置名字
del p.name # 输出:删除名字运行结果如下。
获取名字
Jack
设置名字
删除名字分析:
在上面例子中,property() 函数用于将 get_name()、set_name() 和 del_name() 方法分别绑定到 name 属性的读取、设置和删除操作,从而实现对内部属性 “ _name” 的封装和控制。
当我们访问 name 属性时会自动调用相应的方法,比如读取时会打印 '获取名字',设置时会进行类型检查并打印 '设置名字',删除时会打印 '删除名字'。这种机制不仅隐藏了内部实现细节,还提供了对属性操作的统一接口,增强了代码的安全性和可维护性。
示例 2:使用 @property 代替 property()
class Person:
def __init__(self, name):
self._name = name # 内部存储属性
@property
def name(self):
print('获取名字')
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError('名字必须是字符串')
print('设置名字')
self._name = value
@name.deleter
def name(self):
print('删除名字')
del self._name
p = Person('Jack')
print(p.name) # 输出:获取名字 → Jack
p.name = 'Lucy' # 输出:设置名字
del p.name # 输出:删除名字运行结果如下。
获取名字
Jack
设置名字
删除名字分析:
在上面例子中,@property 用于将方法标记为属性的 getter,@属性名.setter 用于标记 setter,@属性名.deleter 用于标记 deleter。这种方式与 property() 函数的功能是完全一致的。
深入理解 property()
在实际开发中,我们更推荐使用装饰器 @property 来定义属性,而不是使用 property() 函数。这是因为 @property 方式更加简单直观。
1. 数据验证
@property(或 property())最常见的用途就是对属性进行数据验证,以确保属性的值符合特定类型。
示例 3:@property 用于数据校验
class Temperature:
def __init__(self, celsius):
self.celsius = celsius # 初始赋值时会触发 setter,进行验证
@property
def celsius(self):
# 获取当前摄氏温度
return self._celsius
@celsius.setter
def celsius(self, value):
# 设置摄氏温度,并进行类型验证
if not isinstance(value, (int, float)):
raise TypeError('温度必须为数字!')
self._celsius = value
# 正常设置
temp1 = Temperature(25)
print(f'当前温度: {temp1.celsius} 摄氏度')
# 尝试设置无效类型的值
try:
temp2 = Temperature('三十')
except TypeError as e:
print(e)运行结果如下。
当前温度: 25 摄氏度
温度必须为数字!分析:
在这个 Temperature 类中,celsius 属性通过 @property 和 @celsius.setter 实现了对温度值的类型检查。当我们尝试将非数字类型的值(比如'三十')赋值给 celsius 属性,此时就会抛出 TypeError 异常。这种方式不仅增强了代码的健壮性,还隐藏了内部属性的实现细节。
2. 计算属性
@property(或 property())可以用于定义计算属性。所谓的计算属性,指的是属性的值不是直接存储的,而是通过其他属性动态计算得到的。
示例 4:@property 定义计算属性
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
rect = Rectangle(20, 10)
print(rect.area)运行结果如下。
200分析:
在 Rectangle 类中,area 属性通过 @property 装饰器定义。它的值并不是预先存储的,而是在每次访问 rect.area 时,动态地根据 self.width 和 self.height 计算得出的。这保证了 area 属性始终反映最新的宽度和高度,同时避免了数据冗余。
3. 只读属性
通过省略 setter 方法,@property(或 property())可以轻松创建只读属性。所谓的 “只读属性” ,指的是外部代码只能读取属性的值,而不能修改它。
示例 5:@property 定义只读属性
class ReadOnly:
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value
ro = ReadOnly(666)
ro.value = 888运行结果如下。
(报错)AttributeError: property 'value' of 'ReadOnly' object has no setter分析:
在上面定义的 ReadOnly 类中,value 属性只有通过 @property 定义的 getter 方法,而没有对应的 @value.setter 方法。当我们尝试修改 ro.value 时,Python 会检查 value 属性是否定义了 setter,由于没有,就会立即抛出 AttributeError 异常。
常见问题
1. 如何实现类的只读属性?
我们可以仅定义 @property,而不定义 @xxx.setter 方法。
2. 能否在子类中覆盖父类的 property?
可以。子类可以覆盖父类中定义的 property。你需要像在父类中一样,在子类中重新定义 @property 及其相关的 @<attribute_name>.setter 或 @<attribute_name>.deleter 方法。
3. property() 与直接访问属性的性能差异?
property() 调用会有轻微性能损耗,但在大多数场景下可以忽略不计。
