Python property() 函数

Python property() 语法

property() 是 Python 的一个内置函数,它用于在类的定义中添加一个属性。

语法:

property(fget=None, fset=None, fdel=None, doc=None)

说明:

property() 函数接收以下 4 个参数。

  • fget(可选,默认值:None):是用于获取属性值的函数。
  • fset(可选,默认值:None):是用于设置属性值的函数。
  • fdel(可选,默认值:None):是用于删除属性的函数。
  • doc(可选):是属性文档字符串。

property() 在类内部使用,它返回一个属性对象,然后可以赋值给对象实例的属性。

提示:

  • property() 最常见的用法是作为装饰器(@property、@<attribute>.setter、@<attribute>.deleter),这种方式更加简洁和推荐。
  • 使用 property() 可以让你在不破坏现有外部代码的情况下,将公共属性转换为具有 getter / setter 逻辑的 “受控” 属性,这是 Python 优雅的封装机制。

Python property() 摘要

使用频率
官方文档 查看
相关函数 hasattr()getattr()setattr()delattr()

Python property() 示例

接下来,我们通过几个简单的例子来讲解一下 Python property() 函数是如何使用的。

示例 1:property() 基本用法(函数式语法)

class Person:
    def __init__(self, name):
        self._name = name             # 内部存储属性
    
    def get_name(self):
        print('获取名字')
        return self._name
    
    def set_name(self, value):
        if not isinstance(value, str):
            raise ValueError('名字必须是字符串')
        print('设置名字')
        self._name = value
    
    def del_name(self):
        print('删除名字')
        del self._name
    
    # 创建属性对象并绑定方法
    name = property(get_name, set_name, del_name, '姓名属性')

p = Person('Jack')
print(p.name)           # 输出:获取名字 → Jack
p.name = 'Lucy'         # 输出:设置名字
del p.name              # 输出:删除名字

运行结果如下。

获取名字
Jack
设置名字
删除名字

分析:

在上面例子中,property() 函数用于将 get_name()、set_name() 和 del_name() 方法分别绑定到 name 属性的读取、设置和删除操作,从而实现对内部属性 “ _name”  的封装和控制。

当我们访问 name 属性时会自动调用相应的方法,比如读取时会打印 '获取名字',设置时会进行类型检查并打印 '设置名字',删除时会打印 '删除名字'。这种机制不仅隐藏了内部实现细节,还提供了对属性操作的统一接口,增强了代码的安全性和可维护性。

示例 2:使用 @property 代替 property()

class Person:
    def __init__(self, name):
        self._name = name  # 内部存储属性

    @property
    def name(self):
        print('获取名字')
        return self._name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise ValueError('名字必须是字符串')
        print('设置名字')
        self._name = value

    @name.deleter
    def name(self):
        print('删除名字')
        del self._name

p = Person('Jack')
print(p.name)            # 输出:获取名字 → Jack
p.name = 'Lucy'          # 输出:设置名字
del p.name               # 输出:删除名字

运行结果如下。

获取名字
Jack
设置名字
删除名字

分析:

在上面例子中,@property 用于将方法标记为属性的 getter,@属性名.setter 用于标记 setter,@属性名.deleter 用于标记 deleter。这种方式与 property() 函数的功能是完全一致的。

深入理解 property()

在实际开发中,我们更推荐使用装饰器 @property 来定义属性,而不是使用 property() 函数。这是因为 @property 方式更加简单直观。

1. 数据验证

@property(或 property())最常见的用途就是对属性进行数据验证,以确保属性的值符合特定类型。

示例 3:@property 用于数据校验

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius    # 初始赋值时会触发 setter,进行验证

    @property
    def celsius(self):
        # 获取当前摄氏温度
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        # 设置摄氏温度,并进行类型验证
        if not isinstance(value, (int, float)):
            raise TypeError('温度必须为数字!')
        self._celsius = value

# 正常设置
temp1 = Temperature(25)
print(f'当前温度: {temp1.celsius} 摄氏度')

# 尝试设置无效类型的值
try:
    temp2 = Temperature('三十')
except TypeError as e:
    print(e)

运行结果如下。

当前温度: 25 摄氏度
温度必须为数字!

分析:

在这个 Temperature 类中,celsius 属性通过 @property 和 @celsius.setter 实现了对温度值的类型检查。当我们尝试将非数字类型的值(比如'三十')赋值给 celsius 属性,此时就会抛出 TypeError 异常。这种方式不仅增强了代码的健壮性,还隐藏了内部属性的实现细节。

2. 计算属性

@property(或 property())可以用于定义计算属性。所谓的计算属性,指的是属性的值不是直接存储的,而是通过其他属性动态计算得到的。

示例 4:@property 定义计算属性

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    @property
    def area(self):
        return self.width * self.height

rect = Rectangle(20, 10)
print(rect.area)

运行结果如下。

200

分析:

在 Rectangle 类中,area 属性通过 @property 装饰器定义。它的值并不是预先存储的,而是在每次访问 rect.area 时,动态地根据 self.width 和 self.height 计算得出的。这保证了 area 属性始终反映最新的宽度和高度,同时避免了数据冗余。

3. 只读属性

通过省略 setter 方法,@property(或 property())可以轻松创建只读属性。所谓的 “只读属性” ,指的是外部代码只能读取属性的值,而不能修改它。

示例 5:@property 定义只读属性

class ReadOnly:
    def __init__(self, value):
        self._value = value
    
    @property
    def value(self):
        return self._value

ro = ReadOnly(666)
ro.value = 888

运行结果如下。

(报错)AttributeError: property 'value' of 'ReadOnly' object has no setter

分析:

在上面定义的 ReadOnly 类中,value 属性只有通过 @property 定义的 getter 方法,而没有对应的 @value.setter 方法。当我们尝试修改 ro.value 时,Python 会检查 value 属性是否定义了 setter,由于没有,就会立即抛出 AttributeError 异常。

常见问题

1. 如何实现类的只读属性?

我们可以仅定义 @property,而不定义 @xxx.setter 方法。

2. 能否在子类中覆盖父类的 property?

可以。子类可以覆盖父类中定义的 property。你需要像在父类中一样,在子类中重新定义 @property 及其相关的 @<attribute_name>.setter 或 @<attribute_name>.deleter 方法。

3. property() 与直接访问属性的性能差异?

property() 调用会有轻微性能损耗,但在大多数场景下可以忽略不计。

上一篇: super()

下一篇: bin()

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号