@property装饰器

 是 Python 的一个内置装饰器,常用于定义一个类的方法,并将其伪装成“属性”。

  1. 保护类的封装特性
  2. 让开发者可以使用“对象.属性”的方式操作操作类属性

通过 @property 装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对“()”小括号。

语法格式

@property
def 方法名(self)
    代码块

将方法伪装成属性

class Circle:
    def __init__(self, radius):
        self._radius = radius  # 下划线表示私有属性

    @property
    def radius(self):
        # 将该方法伪装成属性
        return self._radius

# 实例化对象
c = Circle(5)
print(c.radius)  # 访问 radius 属性,输出: 5

解释

  • radius 是一个方法,但由于添加了 @property 装饰器,它被当作属性来访问。
  • 访问 c.radius 时,不需要调用 c.radius(),因此代码更简洁。

为属性定义设置器(@property.setter)

默认情况下,使用 @property 定义的属性是“只读”的。如果需要对属性进行赋值,可以使用 @property.setter

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value

# 实例化对象
c = Circle(5)
print(c.radius)  # 访问 radius 属性,输出: 5

c.radius = 10    # 设置 radius 属性
print(c.radius)  # 输出: 10

c.radius = -1    # 尝试赋值负值,抛出 ValueError

解释

  • @radius.setter 为属性提供了一个设置值的入口
  • 在设置 radius 时,代码会检查新值是否满足条件(如是否大于 0),从而保护属性值。

为属性定义删除器(@property.deleter)

如果需要删除某个属性,可以使用 @property.deleter

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value

    @radius.deleter
    def radius(self):
        print("Deleting radius...")
        self._radius = None

# 实例化对象
c = Circle(5)
print(c.radius)  # 输出: 5

del c.radius  # 删除 radius 属性
print(c.radius)  # 输出: None

解释

  • @radius.deleter 定义了删除属性时的行为。
  • del c.radius 会触发 @radius.deleter 中的代码。

 完整示例

import math

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """获取半径"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """设置半径,必须是正数"""
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value

    @property
    def area(self):
        """动态计算面积"""
        return math.pi * self._radius ** 2

    @property
    def circumference(self):
        """动态计算周长"""
        return 2 * math.pi * self._radius

# 创建 Circle 对象
c = Circle(5)

# 获取属性
print("Radius:", c.radius)          # 半径: 5
print("Area:", c.area)              # 面积: 78.53981633974483
print("Circumference:", c.circumference)  # 周长: 31.41592653589793

# 设置属性
c.radius = 10
print("New Radius:", c.radius)      # 新半径: 10
print("New Area:", c.area)          # 新面积: 314.1592653589793

# 尝试设置不合法的值
# c.radius = -5  # 会抛出 ValueError

特点

  • @property 实现了动态计算
    • area 和 circumference 是基于 radius 动态计算的属性,没有直接存储,而是在每次访问时实时计算
  • @radius.setter 对 radius 的赋值进行了验证,确保其始终为正值。
  • 使用这些装饰器,隐藏了底层实现,提供了良好的接口。

 常见应用场景

  1. 动态计算属性值

    使用 @property 定义某些不需要显式存储、但可以通过计算得到的值。例如:
    • 举例:self.area 在每次访问时动态计算,而不是存储在内存中。
    • 圆的面积、周长。
  2. 只读属性

    一些属性不允许直接修改,可以通过 @property 实现只读行为。例如,用于模型的某些统计数据。
  3. 验证输入

    对属性赋值时,利用 @property.setter 检查新值是否合法,防止数据异常。
  4. 延迟加载

    某些资源或对象需要延迟加载(即仅在需要时加载),可以通过 @property 动态创建这些对象。例如:
    • 模型的动态初始化
    • 数据库连接

不用@property的实现

import math

class Circle:
    def __init__(self, radius):
        self._radius = radius  # 私有属性,外部不可直接访问

    # Getter 方法
    def get_radius(self):
        """获取半径"""
        return self._radius

    # Setter 方法
    def set_radius(self, value):
        """设置半径,必须是正数"""
        if value <= 0:
            raise ValueError("Radius must be positive")
        self._radius = value

    # 计算面积
    def get_area(self):
        """动态计算面积"""
        return math.pi * self._radius ** 2

    # 计算周长
    def get_circumference(self):
        """动态计算周长"""
        return 2 * math.pi * self._radius

# 创建 Circle 对象
c = Circle(5)

# 获取半径
print("Radius:", c.get_radius())         # 半径: 5

# 计算面积
print("Area:", c.get_area())             # 面积: 78.53981633974483

# 计算周长
print("Circumference:", c.get_circumference())  # 周长: 31.41592653589793

# 设置新的半径
c.set_radius(10)
print("New Radius:", c.get_radius())     # 新半径: 10
print("New Area:", c.get_area())         # 新面积: 314.1592653589793

# 尝试设置不合法的值
# c.set_radius(-5)  # 会抛出 ValueError: Radius must be positive

 重要应用——延时加载

  • 某些对象或资源(如文件、数据库连接、大型对象等)只有在第一次被访问时才会被加载和初始化。(文件的内容只有在真正需要访问时才被加载。
  • 这样可以避免程序启动时加载所有内容而浪费资源或时间

应用场景——加载大型的数据集(如机器学习中的数据预处理)

class LargeDataset:
    def __init__(self, data_source):
        self.data_source = data_source
        self._data = None  # 大型数据尚未加载

    @property
    def data(self):
        """延迟加载数据"""
        if self._data is None:  # 如果数据未加载
            print("Loading large dataset...")  # 模拟数据加载
            self._data = self._load_data(self.data_source)
        return self._data

    def _load_data(self, source):
        """模拟数据加载的内部方法"""
        return [i for i in range(1000000)]  # 生成一个大数据集

# 使用数据集加载器
dataset = LargeDataset("dummy_source")

# 数据尚未加载
print("Dataset is not loaded yet.")

# 第一次访问触发加载
print(len(dataset.data))  # 输出: 1000000

# 再次访问直接返回已加载的数据
print(len(dataset.data))  # 输出: 1000000

更多解释可以参考:Python @property装饰器详解 - 贾志文 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小老大MUTA️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值