python---封装
文章目录
封装是面向对象编程的三大核心特性之一(封装、继承、多态)。
封装主要有三个目的:
1、数据隐藏:保护对象的内部状态,防止外部直接访问和修改
2、接口暴露:提供受控的访问方式,通过公共方法操作数据
3、实现隔离:将实现细节隐藏,只暴露必要的功能接口
Python 中的封装实现
访问修饰符
Python 使用命名约定来实现访问控制:
class BankAccount:
def __init__(self, account_holder, balance):
self.account_holder = account_holder # 公共属性
self._balance = balance # 受保护的属性(约定)
self.__password = "secret" # 私有属性(名称修饰)
# 公共方法
def get_balance(self):
return self._balance
# 受保护的方法
def _calculate_interest(self):
return self._balance * 0.05
# 私有方法
def __validate_password(self, input_password):
return self.__password == input_password
# 通过公共方法访问私有属性
def withdraw(self, amount, password):
if self.__validate_password(password):
if amount <= self._balance:
self._balance -= amount
return True
return False
私有、保护、公开属性
Python 中没有真正无法访问的“私有”成员,它主要依靠一种叫做“名称修饰”的约定来实现封装,而不是严格的强制访问控制。
这三种访问级别都是通过命名约定来实现的,旨在向开发者传递明确的意图,而不是限制程序的行为。
1. 公开属性
这是默认的访问级别。任何在类中直接定义的属性(变量或方法)都是公开的。
命名方式:没有任何下划线前缀。例如:name, process_data()。
访问权限:可以从类的外部、子类以及其他任何地方自由访问和修改。
代码示例:
class MyClass:
def __init__(self):
self.public_attr = "I am public!" # 公开属性
self.value = 10
def public_method(self): # 公开方法
return "Public method called"
# 外部自由访问
obj = MyClass()
print(obj.public_attr) # 输出: I am public!
obj.public_attr = "Changed"
print(obj.public_attr) # 输出: Changed
print(obj.public_method()) # 输出: Public method called
2. 保护属性
这是一个弱“保护”的指示符。它告诉其他程序员:“这是一个内部属性,除非你知道自己在做什么,否则最好不要在类外部直接访问它。” 但它并不会阻止你访问。
命名方式:以单个下划线开头。例如:_internal_attr, _helper_method()。
访问权限:技术上仍然可以从外部访问,但这是一种约定俗成的信号,意味着“这是内部 API,不稳定,请勿直接使用”。Pylint 等代码检查工具会对此类外部访问发出警告。
from module import * 的影响:使用 from module import * 时,以单下划线开头的变量不会被导入。(不可把其他模块使用)
代码示例:
class MyClass2:
def __init__(self):
self.public = "public"
self._protected_attr = "I am ‘protected‘" # 保护属性
def _protected_method(self): # 保护方法
return "Protected method called"
def use_protected(self):
# 在类的内部,可以自由使用保护属性和方法
print(self._protected_attr)
print(self._protected_method())
# 外部仍然可以访问(但不建议这样做)
obj2 = MyClass2()
print(obj2._protected_attr) # 输出: I am ‘protected‘ (但不推荐)
obj2._protected_attr = "Changed anyway"
print(obj2._protected_attr) # 输出: Changed anyway (但不推荐)
print(obj2._protected_method()) # 输出: Protected method called (但不推荐)
# 推荐的方式是通过公共接口来操作
obj2.use_protected() # 输出: Changed anyway
3. 私有属性
Python 中最强的“私有”指示符。通过一个叫做“名称修饰”的机制来实现,目的是避免在子类中意外重写私有成员。
命名方式:以双下划线开头(但不以双下划线结尾)。例如:__private_attr, __private_method()。
访问权限:Python 解释器会在运行时自动将私有属性的名称修改为 _类名__属性名 的格式。这使得在类外部通过原始名称直接访问变得困难,但如果你知道修饰后的名称,仍然可以访问。这是一种防止意外访问的机制。
class MyClass:
def __init__(self):
self.public = "public"
self._protected = "protected"
self.__private_attr = "I am private" # 私有属性
def __private_method(self): # 私有方法
return "Private method called"
def access_private(self):
# 在类的内部,仍然可以使用原始名称访问私有成员
print(self.__private_attr)
print(self.__private_method())
obj = MyClass()
# 尝试从外部直接访问私有属性(会失败)
# print(obj.__private_attr) # AttributeError: 'MyClass' object has no attribute '__private_attr'
# obj.__private_method() # AttributeError: 'MyClass' object has no attribute '__private_method'
# 查看对象的所有属性,可以看到名称已被修饰
print(dir(obj))
# 输出中会包含: ... '_MyClass__private_attr', '_MyClass__private_method' ...
# 通过修饰后的名称“强行”访问(虽然能做到,但强烈不建议在生产代码中这样做)
print(obj._MyClass__private_attr) # 输出: I am private
print(obj._MyClass__private_method()) # 输出: Private method called
名称修饰的核心作用:防止子类意外重写父类的私有属性。
class Parent:
def __init__(self):
self.__private = "Parent‘s private" # 会被修饰为 _Parent__private
def get_private(self):
return self.__private # 这里访问的是 _Parent__private
class Child(Parent):
def __init__(self):
super().__init__()
self.__private = "Child‘s private" # 会被修饰为 _Child__private。这是一个全新的属性,与父类的无关。
child = Child()
print(child.get_private()) # 输出: "Parent‘s private"
# 因为 Parent 的 get_private 方法寻找的是 _Parent__private,
# 而 Child 实例中的 _Child__private 是完全不同的另一个属性。
总结与对比
| 类型 | 命名约定 | 访问权限 | 主要目的 |
|---|---|---|---|
| 公开 | attribute | 任意位置均可访问 | 类的公共 API,安全使用。 |
| 保护 | _attribute | 仍可访问,但会收到警告,在其他模块不可访问 | 提示开发者“这是内部实现,请勿直接使用,因为它可能改变”。 |
| 私有 | __attribute | 名称被修饰,难以直接访问 | 防止子类意外重写内部属性,实现更强的封装。 |
属性装饰器
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
# Getter 方法
@property
def name(self):
return self._name
# Setter 方法
@name.setter
def name(self, value):
if isinstance(value, str) and len(value) > 0:
self._name = value
else:
raise ValueError("姓名必须是非空字符串")
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if 0 <= value <= 120:
self._age = value
else:
raise ValueError("年龄必须在0-120之间")
# 使用示例
person = Person("张三", 25)
print(person.name) # 通过属性方式访问
person.age = 30 # 通过属性方式设置,会进行验证
只读属性
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""半径(只读)"""
return self._radius
@property
def area(self):
"""面积(计算属性,只读)"""
return 3.14159 * self._radius ** 2
circle = Circle(5)
print(f"半径: {circle.radius}") # 输出: 半径: 5
print(f"面积: {circle.area}") # 输出: 面积: 78.53975
# circle.radius = 10 # 这会报错,因为 radius 是只读的
带验证的 setter
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
"""摄氏温度"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏温度,并进行范围验证"""
if value < -273.15:
raise ValueError("温度不能低于绝对零度(-273.15°C)")
self._celsius = value
@property
def fahrenheit(self):
"""华氏温度(计算属性)"""
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""通过华氏温度设置摄氏温度"""
self.celsius = (value - 32) * 5/9
temp = Temperature(25)
print(f"摄氏: {temp.celsius}°C") # 输出: 摄氏: 25°C
print(f"华氏: {temp.fahrenheit}°F") # 输出: 华氏: 77.0°F
temp.fahrenheit = 100
print(f"摄氏: {temp.celsius}°C") # 输出: 摄氏: 37.77777777777778°C
延迟计算属性
class ExpensiveComputation:
def __init__(self, data):
self._data = data
self._result = None
self._is_computed = False
@property
def result(self):
"""延迟计算的昂贵操作结果"""
if not self._is_computed:
print("正在进行昂贵计算...")
self._result = self._expensive_computation()
self._is_computed = True
return self._result
def _expensive_computation(self):
# 模拟昂贵计算
return sum(x * 2 for x in self._data)
data = [1, 2, 3, 4, 5]
comp = ExpensiveComputation(data)
print("第一次访问:")
print(comp.result) # 会进行计算
print("第二次访问:")
print(comp.result) # 直接返回缓存结果
使用 property() 函数
除了装饰器语法,可以使用 property() 函数:
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
def get_area(self):
return self._width * self._height
def set_width(self, value):
if value <= 0:
raise ValueError("宽度必须大于0")
self._width = value
def get_width(self):
return self._width
# 使用 property() 函数
width = property(get_width, set_width)
area = property(get_area)
rect = Rectangle(10, 5)
print(f"宽度: {rect.width}") # 输出: 宽度: 10
print(f"面积: {rect.area}") # 输出: 面积: 50
rect.width = 15
print(f"新面积: {rect.area}") # 输出: 新面积: 75
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)