面向对象编程的封装特性,是构建健壮、可维护代码的基石。在 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 属性并进行验证。验证检查:
然后创建一个价格为 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_name 和 last_name 的属性(只读属性)。
创建 Person 类的一个实例,并设置以下属性:
first_name 设置为值 ‘John’
last_name 设置为值 ‘Dow’
将该实例的 first_name 和 last_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_name 和 last_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_name 和 last_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 装饰器分别创建 name 和 age 属性(只读属性)。
创建 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 类的一个实例:
将 pet 实例的 __dict__ 属性打印到控制台。然后使用点符号修改属性:
再次将 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 类,该类有两个属性:name 和 age(见下文)。在对象创建和属性修改阶段为 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 的实例:
如果有错误,将错误消息打印到控制台。在解决方案中使用 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 类,该类有两个属性:name 和 age(见下文)。使用名称 pet 和以下属性值创建 Pet 类的一个实例:
然后尝试将 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 属性值:
打印 tech_names 属性的内容。然后,将此属性修改为值:
也将 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 装饰器和访问控制的原理,是掌握面向对象设计模式的重要一环。
掌握这些基础后,你可以尝试更复杂的场景,比如构建具有复杂状态和验证规则的业务模型。如果你在学习过程中有新的想法或遇到问题,欢迎到 云栈社区 与更多开发者交流探讨,共同进步。