Python getter/setter 正确用法详解

在Python开发中,getter(获取器)和setter(设置器)是控制类属性访问的核心技巧,但很多初学者容易陷入两个误区:一是混淆"单下划线属性的访问权限",二是误以为getter/setter是"多余操作"。今天就用最规范的代码示例,带你吃透Python getter/setter的正确用法,避开常见坑点。

先澄清一个关键认知(必看避坑)

很多人误以为"单下划线开头的属性(_attr)是私有属性,外部不能访问",这是错误的!

Python中属性的访问权限约定(无真正"强制私有",靠约定约束):

  • 单下划线 _attr :受保护属性(protected),外部完全可以访问、修改,只是开发者之间的约定------"这是类的内部属性,外部尽量不要直接操作"。

  • 双下划线 __attr:私有属性(private),Python会自动进行"名称改写",外部无法直接通过__attr访问(但仍可通过特殊方式间接访问,本质是一种保护机制,而非绝对禁止)。

而我们使用getter/setter的核心目的,不是为了"禁止访问",而是为了:控制属性的读写逻辑、做数据校验、避免外部直接操作内部属性导致的异常,同时保持外部调用的简洁性。

Python getter/setter 标准实现(@property 装饰器)

Python不推荐像Java那样写getXxx()、setXxx()方法,而是用内置的@property装饰器实现getter和setter,对外表现为"普通属性",对内实现"可控逻辑",优雅又简洁。

下面用3个递进式示例,从基础到进阶,覆盖90%的实际使用场景。

示例1:基础版(只读属性,只有getter)

当属性的值是"计算得出",或者不希望外部修改时,只定义getter,不定义setter,实现"只读"效果。

python 复制代码
class Circle:
    def __init__(self, radius):
        # 单下划线:受保护属性,约定外部不直接操作
        self._radius = radius

    # 定义getter:用@property装饰器,方法名就是对外暴露的属性名
    @property
    def radius(self):
        """获取圆的半径(getter)"""
        return self._radius

    # 定义getter:计算圆的面积(只读,无setter)
    @property
    def area(self):
        """获取圆的面积(只读属性,无法修改)"""
        return 3.14 * self._radius ** 2

# 测试使用
if __name__ == "__main__":
    circle = Circle(5)
    # 访问getter:像普通属性一样,无需加括号
    print(f"圆的半径:{circle.radius}")  # 输出:圆的半径:5
    print(f"圆的面积:{circle.area}")    # 输出:圆的面积:78.5

    # 尝试修改只读属性(无setter),会报错
    # circle.area = 100  # AttributeError: can't set attribute
    # 虽然可以直接修改受保护属性(语法允许),但违反约定,不推荐
    # circle._radius = 10  # 不推荐的操作

核心要点:只有@property装饰的方法,就是getter;无对应的setter时,属性为只读,外部无法赋值。

示例2:进阶版(可读可写,带数据校验)

这是最常用的场景:允许外部修改属性,但需要对修改的值做校验(比如类型、范围限制),避免传入无效数据。此时需要搭配@属性名.setter装饰器实现setter。

python 复制代码
class Student:
    def __init__(self, name, age):
        # 单下划线受保护属性,内部存储真实值
        self._name = name
        self._age = age

    # ------------------------------
    # name的getter和setter
    # ------------------------------
    @property
    def name(self):
        """获取学生姓名(getter)"""
        return self._name

    @name.setter  # 注意:装饰器名称必须和getter方法名一致
    def name(self, value):
        """设置学生姓名(setter),带校验"""
        # 校验1:姓名必须是字符串
        if not isinstance(value, str):
            raise TypeError("姓名必须是字符串类型")
        # 校验2:姓名不能为空,且长度在2-10之间
        if len(value.strip()) not in range(2, 11):
            raise ValueError("姓名长度必须在2-10个字符之间")
        # 校验通过,才赋值给内部属性
        self._name = value.strip()

    # ------------------------------
    # age的getter和setter
    # ------------------------------
    @property
    def age(self):
        """获取学生年龄(getter)"""
        return self._age

    @age.setter
    def age(self, value):
        """设置学生年龄(setter),带校验"""
        # 校验:年龄必须是0-150之间的整数
        if not isinstance(value, int):
            raise TypeError("年龄必须是整数类型")
        if value < 0 or value > 150:
            raise ValueError("年龄必须在0-150之间")
        self._age = value

# 测试使用
if __name__ == "__main__":
    student = Student("张三", 20)
    # 访问getter
    print(f"姓名:{student.name},年龄:{student.age}")  # 输出:姓名:张三,年龄:20

    # 修改属性(自动调用setter,触发校验)
    student.name = "李四"  # 校验通过,正常赋值
    student.age = 22      # 校验通过,正常赋值
    print(f"修改后:姓名:{student.name},年龄:{student.age}")  # 输出:修改后:姓名:李四,年龄:22

    # 测试无效值(触发校验报错)
    # student.name = 123  # TypeError: 姓名必须是字符串类型
    # student.name = "李"  # ValueError: 姓名长度必须在2-10个字符之间
    # student.age = 200   # ValueError: 年龄必须在0-150之间

核心要点:setter的装饰器名称必须和getter方法名完全一致(如@name.setter对应@property def name());setter中可以加入任意校验逻辑,确保数据合法性。

示例3:完整版(可读可写可删除,搭配deleter)

如果需要允许外部"删除"属性,可以搭配@属性名.deleter装饰器,实现属性的删除逻辑(通常用于重置属性值)。

python 复制代码
class User:
    def __init__(self, username, email):
        self._username = username
        self._email = email

    # getter:获取用户名
    @property
    def username(self):
        return self._username

    # setter:设置用户名(带校验)
    @username.setter
    def username(self, value):
        if not value.isalnum():
            raise ValueError("用户名只能包含字母和数字")
        self._username = value

    # deleter:删除用户名(重置为默认值)
    @username.deleter
    def username(self):
        self._username = "default_user"  # 不是真正删除,而是重置
        print("用户名已重置为默认值")

    # email的getter和setter(简化版,无复杂校验)
    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if "@" not in value:
            raise ValueError("邮箱格式不正确")
        self._email = value

# 测试使用
if __name__ == "__main__":
    user = User("zhangsan123", "zhangsan@example.com")
    print(f"用户名:{user.username},邮箱:{user.email}")

    # 修改属性
    user.username = "lisi456"
    user.email = "lisi@example.com"
    print(f"修改后:{user.username},{user.email}")

    # 删除属性(触发deleter)
    del user.username
    print(f"删除后用户名:{user.username}")  # 输出:删除后用户名:default_user

    # 尝试删除无deleter的属性,会报错
    # del user.email  # AttributeError: can't delete attribute

核心要点:deleter不是"真正删除属性",而是执行自定义的重置逻辑;只有定义了@属性名.deleter,才能用del语句操作该属性。

关键总结(吃透核心)

  1. getter/setter 的本质:不是为了"隐藏属性",而是为了"控制属性的读写逻辑",避免无效数据,提升代码健壮性。

  2. 装饰器用法@property 对应getter,@属性名.setter 对应setter,@属性名.deleter 对应deleter,三者名称必须一致。

  3. 属性访问约定:单下划线 _attr 是受保护属性,外部可访问但不推荐直接操作;双下划线 __attr 是私有属性,外部无法直接访问(名称改写)。

  4. 外部调用方式:无论getter/setter/deleter如何定义,外部都像操作普通属性一样(obj.属性 访问、obj.属性=值 修改、del obj.属性 删除),简洁优雅。

常见误区避坑

  • 误区1:认为"单下划线属性不能访问" → 错误,单下划线只是约定,外部可正常访问,只是不推荐直接修改。

  • 误区2:不用@property,直接写get_name()、set_name()方法 → 不推荐,违背Python"优雅简洁"的设计哲学,外部调用繁琐。

  • 误区3:过度使用双下划线 __attr → 没必要,大多数场景下,单下划线+getter/setter足以满足需求,双下划线会增加代码复杂度。

相关推荐
源码之家2 小时前
计算机毕业设计:Python智慧交通大数据分析平台 Flask框架 requests爬虫 出行速度预测 拥堵预测(建议收藏)✅
大数据·hadoop·爬虫·python·数据分析·flask·课程设计
南境十里·墨染春水2 小时前
C++ 笔记 深赋值 浅赋值(面向对象)
开发语言·jvm·c++·笔记
Shaoxi Zhang2 小时前
pm2运行项目实践记录(通过ecosystem.config.js配置并自动运行)
javascript·python·pycharm
华科大胡子2 小时前
开发者的临时文件自动化工具
python
Mr_Xuhhh2 小时前
算法题解合集:回文子串、不相邻取数、空调遥控
python
witAI2 小时前
**AI仿真人剧技术解析2025,专业评估与适配指南**
人工智能·python
Lyyaoo.2 小时前
【JAVA基础面经】JAVA的面向对象特性
java·开发语言·windows
小温冲冲2 小时前
Qt WindowContainer 完整实战示例:QWidget 嵌入 QML
开发语言·数据库·qt
程序设计实验室2 小时前
现代 Python 程序优雅处理日期时间的避坑指南
python