找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2824

积分

0

好友

384

主题
发表于 昨天 11:28 | 查看: 9| 回复: 0

面向对象编程的封装特性,是构建健壮、可维护代码的基石。在 Python 中,实现封装有多种方式,从基础的 getter/setter 方法,到内置的 property 类,再到优雅的 @property 装饰器,它们为属性访问和数据验证提供了不同层级的控制力。

本文将通过一系列循序渐进的练习,手把手带你掌握 Python 封装的各项核心技能。我们将从最简单的属性访问开始,逐步深入到复杂的类型验证与边界控制,让你在动手中理解原理。

练习 55:基础 Getter 与 Setter

实现一个名为 Laptop 的类,在 __init__() 方法中设置存储笔记本电脑价格的受保护属性 _price 的值(不进行任何验证)。然后实现一个名为 get_price() 的方法来读取该属性,以及一个名为 set_price() 的方法来修改该属性(也不进行验证)。

然后创建一个价格为 3499 的 Laptop 类实例,并按以下步骤操作:

  • 使用 get_price() 方法将 _price 受保护属性的值打印到控制台
  • 使用 set_price() 方法将 _price 受保护属性的值设置为 3999
  • 使用 get_price() 方法将 _price 受保护属性的值打印到控制台

预期结果:

3499
3999

练习 56:在 Setter 中添加类型验证

实现了 Laptop 类。实现一个名为 set_price() 的方法来修改 _price 属性并进行验证。验证检查:

  • 值是否为 int 或 float 类型,如果不是则引发 TypeError 并显示以下消息:
    'The price attribute must be an int or float type.'
  • 值是否为正数,如果不是则引发 ValueError 并显示以下消息:
    'The price attribute must be a positive int or float value.'

然后创建一个价格为 3499 的 Laptop 类实例,并尝试使用 set_price() 方法将 _price 设置为 ‘-3000’。如果引发错误,将错误消息打印到控制台。在解决方案中使用 try…except… 子句。

预期结果:

The price attribute must be an int or float type.

练习 57:验证负数值

实现了 Laptop 类。__init__() 方法设置存储笔记本电脑价格的受保护属性 _price 的值(不进行任何验证)。

创建一个价格为 3499 的 Laptop 类实例,并尝试使用 set_price() 方法将 _price 设置为 -3000。如果引发错误,将错误消息打印到控制台。在解决方案中使用 try…except… 子句。

预期结果:

The price attribute must be a positive int or float value.

练习 58:在构造函数中添加验证

实现了 Laptop 类。

在创建实例阶段(__init__() 方法中)也为 _price 属性添加验证。

然后尝试创建一个价格为 -3499 的 Laptop 类实例。如果引发错误,将错误消息打印到控制台。在解决方案中使用 try…except… 子句。

预期结果:

The price attribute must be a positive int or float value.

练习 59:使用 property 类创建只读属性

实现一个名为 Person 的类,该类有一个受保护的实例属性 _first_name。接下来,实现一个 get_first_name() 方法,用于读取 _first_name 受保护属性的值。然后,使用 get_first_name() 方法和 property 类(以标准方式)创建一个名为 first_name 的属性(只读属性)。

创建 Person 类的一个实例,并将 first_name 属性设置为 ‘John’。将该实例的 first_name 属性值打印到控制台。

预期结果:

John
class Laptop:
    def __init__(self, price):
        self._price = price

    def get_price(self):
        return self._price

    def set_price(self, value):
        if not isinstance(value, (int, float)):
            raise TypeError('The price attribute must be an int or float type.')
        if not value > 0:
            raise ValueError('The price attribute must be a positive int or '
                             'float value.')
        self._price = value

练习 60:为多个属性创建只读属性

实现一个名为 Person 的类,该类有两个受保护的实例属性,分别名为 _first_name_last_name。然后实现 get_first_name()get_last_name() 方法,分别读取 _first_name_last_name 受保护属性。

然后,使用 get_first_name()get_last_name() 方法以及 property 类(以标准方式)创建两个名为 first_namelast_name 的属性(只读属性)。

创建 Person 类的一个实例,并设置以下属性:

  • first_name 设置为值 ‘John’
  • last_name 设置为值 ‘Dow’

将该实例的 first_namelast_name 属性值打印到控制台。

预期结果:

John
Dow
class Person:
    def __init__(self, first_name):
        self._first_name = first_name

    def get_first_name(self):
        return self._first_name

    first_name = property(fget=get_first_name)

练习 61:使用 property 类创建读写属性

实现一个名为 Person 的类,该类有两个受保护的实例属性,分别名为 _first_name_last_name。然后实现 get_first_name()get_last_name() 方法,分别读取 _first_name_last_name 受保护属性。

然后,使用 get_first_name()get_last_name() 方法以及 property 类(以标准方式)创建两个名为 first_namelast_name 的属性(只读属性)。

创建 Person 类的一个实例,并将 first_name 属性设置为 ‘John’。然后,使用 set_first_name() 方法设置新值 ‘Mike’

作为响应,将 first_name 属性的值打印到控制台。

预期结果:

Mike
class Person:
    def __init__(self, first_name, last_name):
        self._first_name = first_name
        self._last_name = last_name

    def get_first_name(self):
        return self._first_name

    def get_last_name(self):
        return self._last_name

    first_name = property(fget=get_first_name)
    last_name = property(fget=get_last_name)

练习 62:使用 property 类并修改属性

实现一个名为 Person 的类,该类有两个受保护的实例属性,分别名为 _first_name_last_name。然后实现 get_first_name()get_last_name() 方法,分别读取 _first_name_last_name 受保护属性。

然后,使用 get_first_name()get_last_name() 方法以及 property 类(以标准方式)创建两个名为 first_namelast_name 的属性(只读属性)。

使用以下值创建 Person 类的一个实例:

  • first_name = ‘John’
  • last_name = ‘Dow’

然后按如下所示将这些属性的值打印到控制台。

使用点符号修改该实例的属性值,分别为:

  • first_name 改为值 ‘Tom’
  • last_name 改为值 ‘Smith’

作为响应,将创建的实例的 __dict__ 属性打印到控制台。

预期结果:

John
Dow
{‘_first_name‘: ‘Tom‘, ‘_last_name‘: ‘Smith‘}
class Person:
    def __init__(self, first_name):
        self._first_name = first_name

    def get_first_name(self):
        return self._first_name

    def set_first_name(self, value):
        self._first_name = value

    first_name = property(fget=get_first_name, fset=set_first_name)

练习 63:为 property 添加删除功能

实现了 Person 类。

实现 del_first_name() 方法以删除 _first_name 受保护属性。

然后,使用 get_first_name()set_first_name()del_first_name() 方法和 property 类(以标准方式)创建一个名为 first_name 的属性(用于读取、修改和删除的属性)。

创建 Person 类的一个名为 person 的实例,并将 ‘Tom’ 赋值给 first_name。使用 del_first_name() 方法删除 person 实例的 first_name 属性。将 person 实例的 __dict__ 属性显示到控制台。

预期结果:

{}
class Person:
    def __init__(self, first_name, last_name):
        self._first_name = first_name
        self._last_name = last_name

    def get_first_name(self):
        return self._first_name

    def set_first_name(self, value):
        self._first_name = value

    def get_last_name(self):
        return self._last_name

    def set_last_name(self, value):
        self._last_name = value

    first_name = property(fget=get_first_name, fset=set_first_name)
    last_name = property(fget=get_last_name, fset=set_last_name)

person = Person(‘John‘, ‘Dow‘)
print(person.first_name)
print(person.last_name)
person.first_name = ‘Tom‘
person.last_name = ‘Smith‘
print(person.__dict__)

练习 64:使用 @property 装饰器创建只读属性

实现一个名为 Pet 的类,该类有一个受保护的实例属性 _name。然后实现一个 name() 方法,用于读取受保护的 _name 属性的值。

使用 @property 装饰器创建一个 name 属性(只读)。

创建 Pet 类的一个名为 pet 的实例,并将 name 属性设置为 ‘Max’。作为响应,打印该实例的 __dict__ 属性内容。

预期结果:

{‘_name‘: ‘Max‘}
class Person:
    def __init__(self, first_name):
        self._first_name = first_name

    def get_first_name(self):
        return self._first_name

    def set_first_name(self, value):
        self._first_name = value

    def del_first_name(self):
        del self._first_name

    first_name = property(fget=get_first_name, fset=set_first_name, fdel=del_first_name)

person = Person(‘John‘)
person.del_first_name()
print(person.__dict__)

练习 65:使用多个 @property 装饰器

实现一个名为 Pet 的类,该类有两个受保护的实例属性:_name_age。接下来实现 name()age() 方法,分别读取受保护的 _name_age 属性的值。

使用 @property 装饰器分别创建 nameage 属性(只读属性)。

创建 Pet 类的一个名为 pet 的实例,并将 name 属性设置为 ‘Max’age 设置为 5。

作为响应,将 pet 实例的 __dict__ 属性内容打印到控制台。

预期结果:

{‘_name‘: ‘Max‘, ‘_age‘: 5}

练习 66:使用 @property 和 setter 装饰器

实现一个名为 Pet 的类,该类有一个受保护的实例属性 _name。然后,使用 @property 装饰器创建一个 name 属性(用于读取和修改,不进行验证)。

创建 Pet 类的一个名为 pet 的实例,并将 name 属性设置为 ‘Max’。然后,使用点符号将 name 属性的值修改为 ‘Oscar’

作为响应,打印该实例的 __dict__ 属性内容。

预期结果:

{‘_name‘: ‘Oscar‘}
class Pet:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

pet = Pet(‘Max‘)
print(pet.__dict__)

练习 67:修改多个由 @property 定义的属性

实现一个名为 Pet 的类,该类有一个受保护的实例属性 _name。然后,使用 @property 装饰器创建一个 name 属性(用于读取和修改,不进行验证)。

使用名称 pet 和以下属性创建 Pet 类的一个实例:

  • name = ‘Max’
  • age = 5

pet 实例的 __dict__ 属性打印到控制台。然后使用点符号修改属性:

  • name 改为值 ‘Tom’
  • age 改为值 8

再次将 pet 实例的 __dict__ 属性打印到控制台。

预期结果:

{‘_name‘: ‘Max‘, ‘_age‘: 5}
{‘_name‘: ‘Tom‘, ‘_age‘: 8}
class Pet:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    @property
    def age(self):
        return self._age

pet = Pet(‘Max‘, 5)
print(pet.__dict__)

练习 68:在构造函数中添加属性验证

实现了 Pet 类,该类有两个属性:nameage(见下文)。在对象创建和属性修改阶段为 age 属性添加验证:
age 属性的值必须是 int 类型,否则引发 TypeError 并显示以下消息:

‘The value of age must be of type int.’

age 属性的值必须是正数,否则引发 ValueError 并显示以下消息:

‘The value of age must be a positive integer.’

然后尝试使用以下值创建 Pet 类的一个名为 pet 的实例:

  • ‘Max’
  • ‘seven’

如果有错误,将错误消息打印到控制台。在解决方案中使用 try…except… 子句。

预期结果:

The value of age must be of type int.
class Pet:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

pet = Pet(‘Max‘)
pet.name = ‘Oscar‘
print(pet.__dict__)

练习 69:验证并处理属性设置时的异常

实现了 Pet 类,该类有两个属性:nameage(见下文)。使用名称 pet 和以下属性值创建 Pet 类的一个实例:

  • ‘Max’
  • 7

然后尝试将 age 属性的值修改为 -10。如果有错误,将此错误消息打印到控制台。在解决方案中使用 try…except… 子句。

预期结果:

The value of age must be a positive integer.
class Pet:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        self._age = value

pet = Pet(‘Max‘, 5)
print(pet.__dict__)

pet.name = ‘Tom‘
pet.age = 8
print(pet.__dict__)

练习 70:使用 @property 实现完整属性管理

实现一个名为 TechStack 的类,该类有一个受保护的实例属性 _tech_names。然后,使用 @property 装饰器创建一个 tech_names 属性(读取、修改和删除属性,不进行验证)。

创建该类的一个名为 tech_stack 的实例,并设置 tech_names 属性值:

  • ‘python,java,sql’

打印 tech_names 属性的内容。然后,将此属性修改为值:

  • ‘python,sql’

也将 tech_names 属性的内容打印到控制台。

删除 tech_stack 实例的 tech_names 属性。

tech_stack 实例的 __dict__ 属性内容打印到控制台。

预期结果:

python,java,sql
python,sql
{}

练习 71:实现带有边界验证的属性

实现一个名为 Game 的类,该类有一个名为 level 的属性(读取和修改属性,默认为 0)。level 属性的值应该是 [0, 100] 范围内的整数。在实例创建和属性修改阶段添加验证。如果值不是 int 类型,引发 TypeError 并显示以下消息:

‘The value of level must be of type int.’

如果值超出 [0, 100] 范围,设置超出范围的边界值(分别为 0 或 100)。然后创建一个名为 games 的列表,包含四个 Game 类的实例:

games = [Game(), Game(10), Game(-10), Game(120)]

遍历 games 列表并打印每个实例的 level 属性值。

预期结果:

0
10
0
100
class Pet:
    def __init__(self, name, age):
        self._name = name
        self.age = age

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if not isinstance(value, int):
            raise TypeError(‘The value of age must be of type int.‘)
        if not value > 0:
            raise ValueError(‘The value of age must be a positive integer.‘)
        self._age = value

pet = Pet(‘Max‘, 7)
try:
    pet.age = -10
except TypeError as error:
    print(error)
except ValueError as error:
    print(error)
class TechStack:
    def __init__(self, tech_names):
        self._tech_names = tech_names

    @property
    def tech_names(self):
        return self._tech_names

    @tech_names.setter
    def tech_names(self, value):
        self._tech_names = value

    @tech_names.deleter
    def tech_names(self):
        del self._tech_names

tech_stack = TechStack(‘python,java,sql‘)
print(tech_stack.tech_names)

tech_stack.tech_names = ‘python,sql‘
print(tech_stack.tech_names)

del tech_stack.tech_names
print(tech_stack.__dict__)
class Game:
    def __init__(self, level=None):
        self.level = level if level else 0

    @property
    def level(self):
        return self._level

    @level.setter
    def level(self, value):
        if not isinstance(value, int):
            raise TypeError(‘The value of level must be of type int.‘)
        if value < 0:
            self._level = 0
        elif value > 100:
            self._level = 100
        else:
            self._level = value

games = [Game(), Game(10), Game(-10), Game(120)]
for game in games:
    print(game.level)

通过以上15个由浅入深的练习,我们系统地实践了 Python 封装的多种实现方式。从最基础的显式方法调用,到使用内置的 property 类,再到更现代、更优雅的 @property 装饰器,你不仅学会了语法,更掌握了在属性访问前后嵌入验证逻辑、保证数据完整性的核心思想。

将这些练习融会贯通,你就能在实际项目中设计出更健壮的类。例如,使用 @property 装饰器可以让你像访问普通属性一样调用 getter 和 setter 方法,同时在里面进行类型检查、范围限制等复杂的业务逻辑,这正是 Python 这种动态语言的魅力所在。理解这些 property 装饰器和访问控制的原理,是掌握面向对象设计模式的重要一环。

掌握这些基础后,你可以尝试更复杂的场景,比如构建具有复杂状态和验证规则的业务模型。如果你在学习过程中有新的想法或遇到问题,欢迎到 云栈社区 与更多开发者交流探讨,共同进步。




上一篇:Linux系统无法启动?9大常见场景诊断与修复指南
下一篇:Kotlin Any类型混合序列化方案:Android中Parcelable与Serializable的兼容实践
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 17:36 , Processed in 0.576007 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表