当前位置:首页>python>Python学习--property() 函数详解②

Python学习--property() 函数详解②

  • 2026-10-21 13:28:03
Python学习--property() 函数详解②

上一篇文章主要说明了property() 函数的基本语法相关内容,这次将说明高级用法以及注意事项。

一、高级用法

1. 属性依赖关系

class Rectangle:    def __init__(self, width=None, height=None, area=None):        # 允许用不同方式初始化        if width is not None and height is not None:            self._width = width            self._height = height        elif area is not None and width is not None:            self._width = width            self._height = area / width        elif area is not None and height is not None:            self._height = height            self._width = area / height        else:            raise ValueError("需要提供足够的参数")    @property    def width(self):        return self._width    @width.setter    def width(self, value):        if value <= 0:            raise ValueError("宽度必须大于0")        self._width = value    @property    def height(self):        return self._height    @height.setter    def height(self, value):        if value <= 0:            raise ValueError("高度必须大于0")        self._height = value    @property    def area(self):        return self._width * self._height    @area.setter    def area(self, value):        if value <= 0:            raise ValueError("面积必须大于0")        # 保持宽度不变,调整高度        self._height = value / self._width    @property    def perimeter(self):        return 2 * (self._width + self._height)    @property    def is_square(self):        return self._width == self._height# 使用# 方式1: 通过宽高创建rect1 = Rectangle(width=4, height=5)print(f"矩形1 - 宽: {rect1.width}, 高: {rect1.height}, 面积: {rect1.area}")# 方式2: 通过宽和面积创建rect2 = Rectangle(width=4, area=20)print(f"矩形2 - 宽: {rect2.width}, 高: {rect2.height}, 面积: {rect2.area}")# 修改面积,自动调整高度rect1.area = 30print(f"修改面积后 - 宽: {rect1.width}, 高: {rect1.height}, 面积: {rect1.area}")

2. 使用property实现观察者模式

class ObservableProperty:    """可观察属性"""    def __init__(self, default=None):        self.default = default        self._value = default        self._observers = []    def add_observer(self, callback):        self._observers.append(callback)    def remove_observer(self, callback):        self._observers.remove(callback)    def notify_observers(self, old_value, new_value):        for observer in self._observers:            observer(old_value, new_value)    def __get__(self, obj, objtype):        if obj is None:            return self        return self._value    def __set__(self, obj, value):        old_value = self._value        self._value = value        self.notify_observers(old_value, value)    def __delete__(self, obj):        self._value = self.defaultclass UserSettings:    theme = ObservableProperty("light")    language = ObservableProperty("zh-CN")    font_size = ObservableProperty(14)    def __init__(self):        # 添加属性变化观察者        self.theme.add_observer(self._on_theme_change)        self.language.add_observer(self._on_language_change)    def _on_theme_change(self, old_value, new_value):        print(f"主题从 {old_value} 更改为 {new_value}")    def _on_language_change(self, old_value, new_value):        print(f"语言从 {old_value} 更改为 {new_value}")# 使用settings = UserSettings()print(f"当前主题: {settings.theme}")print(f"当前语言: {settings.language}")settings.theme = "dark"    # 触发通知settings.language = "en"   # 触发通知

3. 属性版本控制

class VersionedProperty:    """带版本控制的属性"""    def __init__(self, default=None):        self.default = default        self._value = default        self._versions = []    def __get__(self, obj, objtype):        if obj is None:            return self        return self._value    def __set__(self, obj, value):        # 记录版本历史        self._versions.append({            'timestamp': '当前时间',  # 实际应用中应该用datetime.now()            'old_value': self._value,            'new_value': value        })        # 最多保留10个版本        if len(self._versions) > 10:            self._versions.pop(0)        self._value = value    @property    def history(self):        """获取版本历史"""        return self._versions.copy()    def revert(self, steps=1):        """回滚到之前的版本"""        if steps >= len(self._versions):            steps = len(self._versions) - 1        for _ in range(steps):            self._versions.pop()        if self._versions:            self._value = self._versions[-1]['new_value']        else:            self._value = self.defaultclass Document:    title = VersionedProperty("未命名文档")    content = VersionedProperty("")    def show_history(self, attr_name):        """显示属性历史"""        prop = getattr(type(self), attr_name)        print(f"{attr_name} 历史:")        for i, version in enumerate(prop.history, 1):            print(f"  版本{i}: {version['old_value']} -> {version['new_value']}")# 使用doc = Document()doc.title = "第一版"doc.title = "第二版"doc.title = "第三版"doc.show_history('title')print(f"当前标题: {doc.title}")# 回滚type(doc).title.revert(1)print(f"回滚后标题: {doc.title}")

二、property与描述符的关系

# property实际上是一个描述符print(type(property))  # <class 'type'># 自定义简化版的propertyclass MyProperty:    def __init__(self, fget=None, fset=None, fdel=None, doc=None):        self.fget = fget        self.fset = fset        self.fdel = fdel        self.__doc__ = doc    def __get__(self, obj, objtype=None):        if obj is None:            return self        if self.fget is None:            raise AttributeError("不可读")        return self.fget(obj)    def __set__(self, obj, value):        if self.fset is None:            raise AttributeError("不可写")        self.fset(obj, value)    def __delete__(self, obj):        if self.fdel is None:            raise AttributeError("不可删除")        self.fdel(obj)    def getter(self, fget):        return type(self)(fget, self.fset, self.fdel, self.__doc__)    def setter(self, fset):        return type(self)(self.fget, fset, self.fdel, self.__doc__)    def deleter(self, fdel):        return type(self)(self.fget, self.fset, fdel, self.__doc__)class Demo:    def __init__(self, x):        self._x = x    @MyProperty    def x(self):        return self._x    @x.setter    def x(self, value):        self._x = value# 使用自定义propertyobj = Demo(10)print(obj.x)  # 输出: 10obj.x = 20print(obj.x)  # 输出: 20

三、最佳实践和注意事项

1. 何时使用property

class GoodUseCase:    """适合使用property的场景"""    def __init__(self, data):        self._data = data    @property    def processed_data(self):        """需要计算的属性"""        return self._data.upper()    @property    def is_valid(self):        """布尔状态属性"""        return len(self._data) > 0class BadUseCase:    """不适合使用property的场景"""    def __init__(self):        self._counter = 0    @property    def increment(self):        """有副作用的方法不应该用property"""        self._counter += 1        return self._counter    # 应该改成普通方法    def increment_counter(self):        self._counter += 1        return self._counter

2. 性能考虑

import timeclass PerformanceTest:    def __init__(self, value):        self._value = value    @property    def value(self):        # 复杂的计算        time.sleep(0.001)  # 模拟耗时操作        return self._value * 2# 在循环中频繁访问property可能影响性能obj = PerformanceTest(10)start = time.time()for _ in range(1000):    _ = obj.valueprint(f"property访问: {time.time() - start:.4f}秒")# 缓存结果可以提高性能start = time.time()cached_value = obj.valuefor _ in range(1000):    _ = cached_valueprint(f"缓存访问: {time.time() - start:.4f}秒")

总结

最佳实践:

  1. 使用@property装饰器语法(更简洁)

  2. 为复杂属性添加文档字符串

  3. 避免在property中做复杂计算

  4. 考虑使用缓存提高性能

  5. 合理使用只读属性

请在微信客户端打开

最新文章

随机文章