文章目录
- 属性封装:将方法伪装成属性,通过点号(.)直接访问 访问控制:自定义属性的 getter、setter 和 deleter 方法 计算属性:动态计算属性值,无需显式存储
- # 方式1:使用 property() 函数 class Person: def __init__(self, age): self._age = age # 私有属性 def get_age(self): return self._age def set_age(self, value): if value < 0: raise ValueError(“年龄不能为负数”) self._age = value age = property(get_age, set_age) # 创建 property 对象 # 方式2:使用装饰器(推荐) class Person: def __init__(self, age): self._age = age @property # 相当于 age = property(age) def age(self): return self._age @age.setter # 相当于 age = age.setter(setter) def age(self, value): if value < 0: raise ValueError(“年龄不能为负数”) self._age = value
- p = Person(25) print(p.age) # 调用 getter 方法,输出: 25 p.age = 30 # 调用 setter 方法 p.age = -5 # 抛出 ValueError: 年龄不能为负数
- class Circle: def __init__(self, radius): self.radius = radius @property def area(self): return 3.14 * self.radius ** 2 c = Circle(5) print(c.area) # 输出: 78.5 c.area = 100 # 报错: AttributeError: can’t set attribute
- class Rectangle: def __init__(self, width, height): self.width = width self.height = height @property def area(self): return self.width * self.height @property def perimeter(self): return 2 * (self.width + self.height) r = Rectangle(3, 4) print(r.area) # 输出: 12 print(r.perimeter) # 输出: 14
- class Person: def __init__(self, name): self._name = name @property def name(self): return self._name @name.deleter def name(self): print(“删除名字…”) del self._name p = Person(“Alice”) print(p.name) # 输出: Alice del p.name # 输出: 删除名字… print(p.name) # 报错: AttributeError
- class Temperature: def __init__(self, celsius): self.celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if not isinstance(value, (int, float)): raise TypeError(“温度必须是数字”) self._celsius = value @property def fahrenheit(self): return self._celsius * 1.8 + 32 t = Temperature(25) print(t.fahrenheit) # 输出: 77.0 t.celsius = “30” # 报错: TypeError
- class User: def __init__(self, db_row): self._db_row = db_row @property def id(self): return self._db_row[“id”] @property def username(self): return self._db_row[“username”] @property def email(self): return self._db_row[“email”] # 使用示例 db_data = {“id”: 1, “username”: “john”, “email”: “john@example.com”} user = User(db_data) print(user.username) # 输出: john
- import math class Factorial: def __init__(self, number): self.number = number self._result = None @property def result(self): if self._result is None: print(“计算阶乘…”) self._result = math.factorial(self.number) return self._result f = Factorial(5) print(f.result) # 输出: 计算阶乘… 120 print(f.result) # 直接返回缓存结果: 120
- class Account: def __init__(self, balance, owner): self._balance = balance self._owner = owner @property def balance(self): return self._balance @balance.setter def balance(self, value): if self._owner != “admin”: raise PermissionError(“只有管理员可以修改余额”) self._balance = value acc = Account(1000, “user”) print(acc.balance) # 输出: 1000 acc.balance = 2000 # 报错: PermissionError
- property 是一个描述符(descriptor)类,其简化实现如下: class Property: def __init__(self, fget=None, fset=None, fdel=None): self.fget = fget self.fset = fset self.fdel = fdel def __get__(self, instance, owner): if instance is None: return self if self.fget is None: raise AttributeError(“unreadable attribute”) return self.fget(instance) def __set__(self, instance, value): if self.fset is None: raise AttributeError(“can’t set attribute”) self.fset(instance, value) def __delete__(self, instance): if self.fdel is None: raise AttributeError(“can’t delete attribute”) self.fdel(instance) def getter(self, fget): return type(self)(fget, self.fset, self.fdel) def setter(self, fset): return type(self)(self.fget, fset, self.fdel) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel)
- 子类可重写父类的 property: class Parent: @property def value(self): return 10 class Child(Parent): @property def value(self): return 20
- 少量调用:property 的性能开销极小(约 100ns / 次) 大量调用:若性能敏感(如循环百万次),建议直接访问属性
- 推荐场景:需要验证、计算或访问控制的属性 不推荐场景:简单的数据封装(直接使用公有属性)
- 使用单下划线 _attr 表示受保护属性(非强制私有) 使用双下划线 __attr 进行名称修饰(Name Mangling)
- class Person: def __init__(self, age): self.age = age # 调用 setter 进行验证 @property def age(self): return self._age @age.setter def age(self, value): if value < 0: raise ValueError(“年龄不能为负数”) self._age = value
目录
- 一、核心概念与基础语法
- 1.property的本质
- 2. 基础语法(两种方式)
- 3. 使用示例
- 二、进阶用法
- 1. 只读属性(仅实现 getter)
- 2. 计算属性(动态计算)
- 3. 删除属性(实现 deleter)
- 4. 类型检查与验证
- 三、实战案例
- 1. 数据库字段映射
- 2. 缓存计算结果
- 3. 权限控制
- 四、深入理解与注意事项
- 1.property的底层实现
- 2. 继承与property
- 3. 性能考量
- 五、与其他技术的对比
- 六、常见误区与最佳实践
- 1. 避免过度使用property
- 2. 私有属性命名约定
- 3. 初始化时调用 setter
- 七、总结
在 Python 中,property 是一个内置的装饰器,用于将类方法转换为类属性,实现对属性的高级控制(如类型检查、只读限制、计算属性等)。
以下是对 property 的详细解析与实战案例:
- 属性封装:将方法伪装成属性,通过点号(
.)直接访问
- 访问控制:自定义属性的 getter、setter 和 deleter 方法
- 计算属性:动态计算属性值,无需显式存储
.)直接访问
# 方式1:使用 property() 函数
class Person:
def __init__(self, age):
self._age = age # 私有属性
def get_age(self):
return self._age
def set_age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
age = property(get_age, set_age) # 创建 property 对象
# 方式2:使用装饰器(推荐)
class Person:
def __init__(self, age):
self._age = age
@property # 相当于 age = property(age)
def age(self):
return self._age
@age.setter # 相当于 age = age.setter(setter)
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
p = Person(25)
print(p.age) # 调用 getter 方法,输出: 25
p.age = 30 # 调用 setter 方法
p.age = -5 # 抛出 ValueError: 年龄不能为负数
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14 * self.radius ** 2
c = Circle(5)
print(c.area) # 输出: 78.5
c.area = 100 # 报错: AttributeError: can't set attribute
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)
r = Rectangle(3, 4)
print(r.area) # 输出: 12
print(r.perimeter) # 输出: 14
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.deleter
def name(self):
print("删除名字...")
del self._name
p = Person("Alice")
print(p.name) # 输出: Alice
del p.name # 输出: 删除名字...
print(p.name) # 报错: AttributeError
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if not isinstance(value, (int, float)):
raise TypeError("温度必须是数字")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 1.8 + 32
t = Temperature(25)
print(t.fahrenheit) # 输出: 77.0
t.celsius = "30" # 报错: TypeError
class User:
def __init__(self, db_row):
self._db_row = db_row
@property
def id(self):
return self._db_row["id"]
@property
def username(self):
return self._db_row["username"]
@property
def email(self):
return self._db_row["email"]
# 使用示例
db_data = {"id": 1, "username": "john", "email": "john@example.com"}
user = User(db_data)
print(user.username) # 输出: john
import math
class Factorial:
def __init__(self, number):
self.number = number
self._result = None
@property
def result(self):
if self._result is None:
print("计算阶乘...")
self._result = math.factorial(self.number)
return self._result
f = Factorial(5)
print(f.result) # 输出: 计算阶乘... 120
print(f.result) # 直接返回缓存结果: 120
class Account:
def __init__(self, balance, owner):
self._balance = balance
self._owner = owner
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
if self._owner != "admin":
raise PermissionError("只有管理员可以修改余额")
self._balance = value
acc = Account(1000, "user")
print(acc.balance) # 输出: 1000
acc.balance = 2000 # 报错: PermissionError
property 是一个描述符(descriptor)类,其简化实现如下:
class Property:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
if instance is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(instance)
def __set__(self, instance, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance, value)
def __delete__(self, instance):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(instance)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel)
- 子类可重写父类的 property:
class Parent:
@property
def value(self):
return 10
class Child(Parent):
@property
def value(self):
return 20
class Parent:
@property
def value(self):
return 10
class Child(Parent):
@property
def value(self):
return 20
- 少量调用:
property 的性能开销极小(约 100ns / 次)
- 大量调用:若性能敏感(如循环百万次),建议直接访问属性
property 的性能开销极小(约 100ns / 次)
| 技术 | 用途 | 实现方式 |
|---|---|---|
| property | 属性封装与控制 | 装饰器或 property () 函数 |
| getter/setter | 传统的访问控制方法 | 显式定义方法 |
| getattr | 动态属性获取 | 魔法方法(所有属性都经过它) |
| setattr | 动态属性设置 | 魔法方法(所有属性都经过它) |
- 推荐场景:需要验证、计算或访问控制的属性
- 不推荐场景:简单的数据封装(直接使用公有属性)
- 使用单下划线
_attr 表示受保护属性(非强制私有)
- 使用双下划线
__attr 进行名称修饰(Name Mangling)
_attr 表示受保护属性(非强制私有)__attr 进行名称修饰(Name Mangling)
class Person:
def __init__(self, age):
self.age = age # 调用 setter 进行验证
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负数")
self._age = value
| 场景 | 推荐实现方式 |
|---|---|
| 简单属性 | 直接使用公有属性 |
| 需要类型检查 / 验证 | 使用 property 装饰器 |
| 只读属性 | 只实现 getter 方法 |
| 计算属性 | 使用 property 装饰器 |
| 复杂的属性访问控制 | 自定义描述符类 |
合理使用 property 可以提高代码的安全性、可维护性和可读性,尤其在需要控制属性访问逻辑的场景中表现出色。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。
您可能感兴趣的文章:
- Python内置函数property()如何使用
- Python中lazy property的两种方法小结
- python使用property完成数据隐藏封装与校验
- Python property函数的具体使用
- 掌握Python property装饰器巧妙管理类的属性
- python装饰器中@property属性的使用解析
- Python中通过property设置类属性的访问