property() 函数是 Python 中用于创建属性访问器的内置函数,它允许你将方法作为属性来访问,实现数据封装和属性控制。
一、property() 的基本用法
1. 基本语法
property(fget=None, fset=None, fdel=None, doc=None)
fget: 获取属性值的方法
fset: 设置属性值的方法
fdel: 删除属性的方法
doc: 属性文档字符串
2. 基本示例
class Person: def __init__(self, name): self._name = name # 私有属性 def get_name(self): """获取姓名""" print("获取姓名") return self._name def set_name(self, value): """设置姓名""" print(f"设置姓名为: {value}") if not isinstance(value, str): raise TypeError("姓名必须是字符串") self._name = value def del_name(self): """删除姓名""" print("删除姓名") del self._name # 创建属性 name = property(get_name, set_name, del_name, "姓名属性")# 使用person = Person("张三")# 获取属性(调用get_name)print(person.name) # 输出: 张三# 设置属性(调用set_name)person.name = "李四"# 删除属性(调用del_name)del person.name# 查看文档print(Person.name.__doc__) # 输出: 姓名属性
二、使用装饰器语法(推荐)
1. 完整的属性装饰器
class Temperature: def __init__(self, celsius=0): self._celsius = celsius # 私有属性 @property def celsius(self): """摄氏温度(获取器)""" print("获取摄氏温度") return self._celsius @celsius.setter def celsius(self, value): """摄氏温度(设置器)""" print(f"设置摄氏温度为: {value}") if value < -273.15: raise ValueError("温度不能低于绝对零度(-273.15°C)") self._celsius = value @celsius.deleter def celsius(self): """删除摄氏温度""" print("删除温度数据") del self._celsius @property def fahrenheit(self): """华氏温度(只读属性)""" print("计算华氏温度") return self._celsius * 9/5 + 32# 使用temp = Temperature(25)print(f"摄氏温度: {temp.celsius}°C") # 调用getterprint(f"华氏温度: {temp.fahrenheit}°F") # 只读属性temp.celsius = 30 # 调用setterprint(f"修改后华氏温度: {temp.fahrenheit}°F")# temp.fahrenheit = 100 # 错误:只读属性没有setter# del temp.fahrenheit # 错误:只读属性没有deleterdel temp.celsius # 调用deleter
2. 实际应用示例
class BankAccount: def __init__(self, owner, initial_balance=0): self.owner = owner self._balance = initial_balance self._transactions = [] @property def balance(self): """账户余额(只读)""" return self._balance @property def is_overdrawn(self): """是否透支(计算属性)""" return self._balance < 0 @property def transaction_count(self): """交易次数""" return len(self._transactions) def deposit(self, amount): """存款""" if amount <= 0: raise ValueError("存款金额必须大于0") self._balance += amount self._transactions.append(('存款', amount)) return self._balance def withdraw(self, amount): """取款""" if amount <= 0: raise ValueError("取款金额必须大于0") if amount > self._balance: raise ValueError("余额不足") self._balance -= amount self._transactions.append(('取款', amount)) return self._balance# 使用account = BankAccount("张三", 1000)print(f"余额: {account.balance}") # 只读属性print(f"是否透支: {account.is_overdrawn}") # 计算属性print(f"交易次数: {account.transaction_count}")account.deposit(500)account.withdraw(200)print(f"最终余额: {account.balance}")print(f"最终交易次数: {account.transaction_count}")# account.balance = 2000 # 错误:只读属性
三、属性验证和数据保护
1. 数据验证属性
class Student: def __init__(self, name): self._name = name self._age = None self._score = None @property def name(self): return self._name @name.setter def name(self, value): if not isinstance(value, str): raise TypeError("姓名必须是字符串") if len(value.strip()) == 0: raise ValueError("姓名不能为空") self._name = value @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 isinstance(value, (int, float)): raise TypeError("成绩必须是数字") if value < 0 or value > 100: raise ValueError("成绩必须在0-100之间") self._score = value @property def grade(self): """计算属性:根据成绩返回等级""" if self._score is None: return "未评分" elif self._score >= 90: return "优秀" elif self._score >= 80: return "良好" elif self._score >= 60: return "及格" else: return "不及格"# 使用student = Student("张三")student.age = 20student.score = 85print(f"姓名: {student.name}")print(f"年龄: {student.age}")print(f"成绩: {student.score}")print(f"等级: {student.grade}")# 测试验证try: student.age = 200 # 错误:年龄超限except ValueError as e: print(f"错误: {e}")try: student.score = "优秀" # 错误:类型不匹配except TypeError as e: print(f"错误: {e}")
2. 惰性计算属性
import mathclass Circle: def __init__(self, radius): self.radius = radius self._area = None # 缓存区域 self._circumference = None # 缓存周长 @property def radius(self): return self._radius @radius.setter def radius(self, value): if value <= 0: raise ValueError("半径必须大于0") self._radius = value # 当半径改变时,清除缓存 self._area = None self._circumference = None @property def area(self): """惰性计算面积(缓存结果)""" if self._area is None: print("计算面积...") self._area = math.pi * self._radius ** 2 return self._area @property def circumference(self): """惰性计算周长(缓存结果)""" if self._circumference is None: print("计算周长...") self._circumference = 2 * math.pi * self._radius return self._circumference# 使用circle = Circle(5)print(f"半径: {circle.radius}")print(f"面积: {circle.area}") # 第一次计算print(f"面积: {circle.area}") # 使用缓存print(f"周长: {circle.circumference}") # 第一次计算print(f"周长: {circle.circumference}") # 使用缓存# 改变半径,缓存失效circle.radius = 10print(f"\n新面积: {circle.area}") # 重新计算
总结
property的核心价值:
✅ 数据封装:隐藏实现细节
✅ 数据验证:在设置属性时进行验证
✅ 计算属性:将方法作为属性访问
✅ 惰性计算:延迟计算直到需要时
✅ 向后兼容:可以在不改变接口的情况下修改实现
property是Python面向对象编程中非常重要的特性,合理使用可以使代码更加优雅和安全!