Python:property装饰器的作用
@property
装饰器可以将方法转换为属性,便于对属性的访问控制。
class Person:
def __init__(self, name):
self._name = name # 设为受保护属性(单下划线约定)
@property # 将方法转换为属性的 getter
def name(self):
return self._name # 实际返回的是受保护属性
@name.setter # 定义属性的 setter
def name(self, value):
if len(value) >= 2:
self._name = value
else:
raise ValueError("Name is too short")
person = Person("Alice")
print(person.name) # 输出:Alice
person.name = "Bob"
print(person.name) # 输出:Bob
关键:
- 使用
@property
可以在不改变调用方式的情况下,对属性的访问进行控制。 - 可以在
setter
方法中添加验证逻辑。
二、核心机制
1. 将方法伪装成属性访问
-
普通方法调用:通常需要
obj.method()
的形式 -
@property
魔法:让方法可以通过obj.property
直接访问
person = Person("Alice")
print(person.name) # 看起来像访问属性,实际调用了 @property 修饰的 name() 方法
2. 实现访问控制
-
Setter 验证:通过
@name.setter
控制赋值逻辑
person.name = "B" # 触发 setter,因长度不足抛出 ValueError
3. 隐藏底层存储
-
数据实际存储在
self._name
-
外部使用者只感知
name
属性,不知内部实现细节
print(person.__dict__) # 输出:{'_name': 'Bob'}(真实存储位置)
三、@property
的作用总结
特性 | 说明 |
---|---|
属性化方法调用 | 用 obj.property 替代 obj.method() ,更符合直觉 |
数据验证 | 在 setter 中检查数据合法性(如类型、范围、格式) |
访问隔离 | 外部不直接操作实际存储的 _name ,保护数据完整性 |
动态计算属性 | getter 可以实时计算值(例如根据其他属性生成结果) |
接口兼容性 | 后续修改 _name 的存储方式时,外部调用接口无需变更 |
四、对比直接操作属性
没有 @property
的问题
class Person:
def __init__(self, name):
self.name = name # 直接暴露属性
# 外部可以随意设置非法值
person = Person("A") # 短名字直接通过,没有验证
使用 @property
的优势
person = Person("Alice")
person.name = "A" # 自动触发 setter 验证,抛出 ValueError
五、进阶用法
1. 只读属性(无 setter)
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def diameter(self):
return self.radius * 2 # 动态计算,无法直接修改
circle = Circle(5)
print(circle.diameter) # 输出:10
circle.diameter = 20 # 报错:AttributeError(没有 setter)
2. 删除控制(@xxx.deleter
)
class File:
def __init__(self, path):
self.path = path
@property
def path(self):
return self._path
@path.deleter
def path(self):
print("删除前清理资源")
del self._path
file = File("test.txt")
del file.path # 触发 deleter
3. 与私有属性配合
class SafeData:
def __init__(self):
self.__secret = 42 # 私有属性
@property
def secret(self):
# 可在此处添加访问日志、权限验证等
return self.__secret * 2 # 返回加工后的数据
data = SafeData()
print(data.secret) # 输出:84(外部无法直接访问 __secret)