掌握Python中的访问控制机制,是深入理解面向对象编程不可或缺的一环。无论是公有(Public)、保护(Protected)还是私有(Private)属性,它们如何在内部存储与访问,常常困扰着初学者。今天,我们就通过几个循序渐进的练习,来揭开这些属性在类实例中的真实面貌。
练习51:实例化与属性映射
实现一个名为 Laptop 的类,在创建实例时设置以下实例属性:
brand 作为公有实例属性
model 作为受保护属性
price 作为私有属性
然后创建一个名为 laptop 的实例,具有以下参数:
作为响应,打印 laptop 实例的 __dict__ 属性值。
预期结果:
{'brand': 'Acer', '_model': 'Predator', '_Laptop__price': 5490}
想要完成这个练习,我们需要理解Python中受保护属性和私有属性的命名约定。受保护属性通常以一个下划线 _ 开头,而私有属性则以双下划线 __ 开头。实例的 __dict__ 属性会返回一个包含其所有实例属性和对应值的字典,从中我们可以直观地看到这些属性在内部的实际存储名称。
根据要求,我们可以这样实现类:
class Laptop:
def __init__(self, brand, model, price):
self.brand = brand
self._model = model
self.__price = price
laptop = Laptop('Acer', 'Predator', 5490)
print(laptop.__dict__)
运行这段代码,你将得到与预期一致的结果。注意观察私有属性 price 在 __dict__ 中是如何被“名称修饰”(Name Mangling)为 _Laptop__price 的。
练习52:访问不同可见性的属性
继续使用上面实现的 Laptop 类。然后,使用以下参数创建 Laptop 类的一个名为 laptop 的实例:
作为响应,按如下所示打印 laptop 实例的每个实例属性值(每行一个)。
预期结果:
brand -> Acer
model -> Predator
price -> 5490
这个练习的关键在于如何正确地访问私有属性。公有属性 brand 可以直接通过 laptop.brand 访问,受保护属性 model 可以通过 laptop._model 访问(尽管约定上不建议在类外部直接访问)。那么私有属性 price 呢?根据上一个练习的发现,我们需要使用被修饰后的名称 _Laptop__price。
因此,代码实现如下:
class Laptop:
def __init__(self, brand, model, price):
self.brand = brand
self._model = model
self.__price = price
laptop = Laptop('Acer', 'Predator', 5490)
print(f'brand -> {laptop.brand}')
print(f'model -> {laptop._model}')
print(f'price -> {laptop._Laptop__price}')
执行后,你就能成功输出所有属性的值。这清晰地展示了私有属性的访问方式,它并非完全不可访问,而是通过一个特定的、与类名关联的名称来实现的。
练习53:筛选并显示私有属性
给出了 Laptop 类的 __init__ 方法实现如下:
def __init__(self, brand, model, code, price, margin):
self.brand = brand
self._model = model
self._code = code
self.__price = price
self.__margin = margin
现在,需要在 Laptop 类中实现一个名为 display_private_attrs() 的方法,该方法显示实例的所有私有属性名称。然后使用以下参数创建一个实例:
'Acer'
'Predator'
'AC-100'
5490
0.2
并将其赋值给变量 laptop。作为响应,在 laptop 实例上调用 display_private_attrs()。
预期结果:
_Laptop__price
_Laptop__margin
这个练习要求我们编写一个方法来自动识别私有属性。思路是遍历实例的 __dict__,找出那些经过名称修饰的属性。在 Python 中,私有属性的修饰规则是在属性名前加上 _类名。
所以,我们可以这样实现 display_private_attrs 方法:
class Laptop:
def __init__(self, brand, model, code, price, margin):
self.brand = brand
self._model = model
self._code = code
self.__price = price
self.__margin = margin
def display_private_attrs(self):
for attr in self.__dict__:
if attr.startswith('_'+self.__class__.__name__+'_'):
print(attr)
laptop = Laptop('Acer', 'Predator', 'AC-100', 5490, 0.2)
laptop.display_private_attrs()
该方法会检查 __dict__ 中的每个键(属性名),如果它是以 _Laptop_ 开头的,则判定为私有属性并打印出来。
练习54:筛选并显示受保护属性
给出了 Laptop 类的 __init__ 方法实现如下:
class Laptop:
def __init__(self, brand, model, code, price, margin):
self.brand = brand
self._model = model
self._code = code
self.__price = price
self.__margin = margin
在 Laptop 类中实现一个名为 display_protected_attrs() 的方法,该方法显示实例的所有受保护属性名称。然后使用以下参数创建一个实例:
'Acer'
'Predator'
'AC-100'
5490
0.2
并将其赋值给变量 laptop。作为响应,在 laptop 实例上调用 display_protected_attrs()。
预期结果:
_model
_code
这个练习与上一个类似,但目标是筛选受保护属性。受保护属性的命名约定是:以一个下划线 _ 开头,但并非以双下划线 __ 开头(除非是像 __init__ 这样的特殊方法,但它们通常不存储在 __dict__ 里)。
因此,我们的判断逻辑是:属性名以 _ 开头,且不以 __ 开头。注意,我们也需要排除那些私有属性(即以 _类名__ 开头的),因为它们也符合“以 _ 开头”的条件。
实现代码如下:
class Laptop:
def __init__(self, brand, model, code, price, margin):
self.brand = brand
self._model = model
self._code = code
self.__price = price
self.__margin = margin
def display_protected_attrs(self):
for attr in self.__dict__:
# 以一个下划线开头,并且不是以“_类名__”这种私有属性格式开头
if attr.startswith('_') and not attr.startswith('__' + self.__class__.__name__ + '__'):
print(attr)
laptop = Laptop('Acer', 'Predator', 'AC-100', 5490, 0.2)
laptop.display_protected_attrs()
运行后,将正确地输出 _model 和 _code。
通过以上四个循序渐进的练习,我们不仅动手编写了代码,还深入探究了Python中不同类型属性的内部存储机制和访问方式。理解这些细节,能帮助你在未来的后端与架构设计和类封装中做出更明智的决策。如果想与更多开发者交流类似的心得或挑战,欢迎来云栈社区一起探讨。