Python property装饰器详解
·
Python @property 装饰器详解
@property 是 Python 中用于实现属性访问控制的内置装饰器,它可以让你像访问属性一样调用方法。
基本概念
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
"""获取姓名"""
return self._name
@name.setter
def name(self, value):
"""设置姓名"""
if not isinstance(value, str):
raise TypeError("姓名必须是字符串")
self._name = value
@name.deleter
def name(self):
"""删除姓名"""
del self._name
# 使用示例
p = Person("张三")
print(p.name) # 张三 - 像属性一样访问
p.name = "李四" # 像属性一样赋值
print(p.name) # 李四
del p.name # 像属性一样删除
三种装饰器
1. @property - getter
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""获取半径"""
return self._radius
@property
def area(self):
"""计算面积 - 只读属性"""
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.radius) # 5
print(c.area) # 78.53975
# c.area = 100 # 错误!AttributeError: can't set attribute
2. @方法名.setter - setter
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
"""只读属性"""
return self._celsius * 9/5 + 32
t = Temperature(25)
t.celsius = 30 # 使用setter
print(t.celsius) # 30
# t.celsius = -300 # ValueError!
3. @方法名.deleter - deleter
class User:
def __init__(self, username):
self._username = username
self._password = "secret"
@property
def password(self):
return self._password
@password.deleter
def password(self):
"""删除密码"""
print("正在删除密码...")
del self._password
user = User("alice")
print(user.password) # secret
del user.password # 正在删除密码...
# print(user.password) # AttributeError
实际应用场景
1. 数据验证
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value or not value.strip():
raise ValueError("姓名不能为空")
self._name = value.strip()
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise TypeError("年龄必须是整数")
if value < 0 or value > 150:
raise ValueError("年龄必须在0-150之间")
self._age = value
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not 0 <= value <= 100:
raise ValueError("分数必须在0-100之间")
self._score = value
@property
def grade(self):
"""只读属性 - 根据分数计算等级"""
if self._score >= 90:
return "A"
elif self._score >= 80:
return "B"
elif self._score >= 70:
return "C"
elif self._score >= 60:
return "D"
else:
return "F"
s = Student("张三", 18, 85)
print(f"{s.name}, {s.age}岁, 成绩{s.score}, 等级{s.grade}")
2. 延迟计算/缓存
class DataProcessor:
def __init__(self, data):
self.data = data
self._processed = None
@property
def processed_data(self):
"""延迟计算,结果会被缓存"""
if self._processed is None:
print("正在处理数据...")
self._processed = [x * 2 for x in self.data]
return self._processed
dp = DataProcessor([1, 2, 3, 4, 5])
print(dp.processed_data) # 第一次:正在处理数据... [2,4,6,8,10]
print(dp.processed_data) # 第二次:直接返回 [2,4,6,8,10](不打印)
3. 属性依赖更新
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
self._area = None
@property
def width(self):
return self._width
@width.setter
def width(self, value):
self._width = value
self._area = None # 清除缓存
@property
def height(self):
return self._height
@height.setter
def height(self, value):
self._height = value
self._area = None
@property
def area(self):
if self._area is None:
self._area = self._width * self._height
return self._area
r = Rectangle(5, 3)
print(r.area) # 15(计算)
r.width = 10
print(r.area) # 30(重新计算)
使用 @property 的优点
1. 向后兼容
# 最初版本 - 直接使用属性
class PersonV1:
def __init__(self, name):
self.name = name
# 后续版本 - 需要添加验证,但不影响现有代码
class PersonV2:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("Name cannot be empty")
self._name = value
# 现有代码 p.name = "张三" 仍然可以正常工作
2. 封装逻辑
class BankAccount:
def __init__(self, balance=0):
self._balance = balance
self._transactions = []
@property
def balance(self):
"""只读属性"""
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("存款金额必须为正数")
self._balance += amount
self._transactions.append(f"存款: +{amount}")
def withdraw(self, amount):
if amount <= 0:
raise ValueError("取款金额必须为正数")
if amount > self._balance:
raise ValueError("余额不足")
self._balance -= amount
self._transactions.append(f"取款: -{amount}")
@property
def transaction_history(self):
"""只读属性"""
return self._transactions.copy()
acc = BankAccount(1000)
acc.deposit(500)
acc.withdraw(200)
print(f"余额: {acc.balance}") # 1300
print(f"交易记录: {acc.transaction_history}")
# acc.balance = 9999 # 错误!无法直接修改
注意事项
-
命名约定:通常使用下划线前缀(
_)表示私有属性 -
性能考虑:
@property方法会有轻微的性能开销 -
不要滥用:简单的属性访问不需要使用
@property -
继承行为:
@property会被子类继承
class Parent:
@property
def value(self):
return 10
class Child(Parent):
@property
def value(self):
return 20 # 可以覆盖
c = Child()
print(c.value) # 20
总结
@property 是 Python 中实现封装和数据验证的优雅方式,它让你能够:
-
在访问属性时添加逻辑(验证、计算等)
-
创建只读属性
-
实现延迟计算
-
保持向后兼容性
-
提供清晰的 API 设计
更多推荐



所有评论(0)