封装
大家好,这个章节我们来讲解封装这个面向对象编程的核心概念。
简介
封装是面向对象编程中三大特征之一,指的是将数据和操作数据的方法打包在一起,形成一个类或对象。
封装的目的是隐藏对象的内部实现细节,提供一个安全且易于使用的接口,使得对象之间的交互更加简单和可靠。
简单来说,封装就是把数据和操作这些数据的方法打包到一起,形成一个类或对象。你可以把它理解为把所有相关的东西捆绑在一起,这样就不会乱七八糟地散落在不同地方。封装的主要目的是隐藏对象的内部实现细节,让外部使用者只需要知道如何与对象进行交互,而不需要知道它内部是怎么工作的。这样一来,代码更安全、简洁,也更容易维护。
封装主要包括以下几个方面的内容:
-
数据隐藏:通过将对象的数据属性设置为私有或受保护的,防止外部直接访问和修改对象的数据。这样可以确保对象的数据在被操作时不会被意外篡改或破坏。
-
方法封装:将对象对自身数据的操作封装在方法中,只通过方法来访问和修改对象的数据。这样可以确保对对象的操作符合预期,避免了外部错误地修改对象的数据。
-
接口定义:通过定义公共接口,将对象的功能暴露给外部使用者。使用者只需关心如何使用接口提供的方法,而不需要了解内部实现细节。这样可以提高代码的可读性和可维护性,同时也能够实现代码的模块化和复用。
封装主要有几个关键点:首先是数据隐藏:通过把类的数据属性设置成私有或受保护的,防止外部直接访问和修改数据,避免数据被意外修改。然后是方法封装:通过将数据操作封装在方法中,我们确保数据只能通过特定的途径进行访问和修改,保证了操作的正确性。最后是接口定义:通过定义公共接口,让外部使用者只关心如何调用这些接口,而不需要了解实现的细节。
访问控制
在 Python 中并没有像 Java,C++ 一样,提供了 public, protected, private 这样的访问控制修饰符,Python 通过一种称为 名称改写 的方式,实现其它语言中访问控制修饰符的作用。
但是要注意的是,在 Python 中 名称改写 只是一种约定,并没有真正的实现私有的作用,在 Python 中只要想访问,所有的数据都可以拿到,获取方法在这里不讨论。
在 Python 中,我们没有像 Java 或 C++ 那样提供明确的访问控制修饰符,比如 public, protected, private。不过,Python 通过一种叫做“名称改写”的方式,模拟了这些修饰符的功能。需要注意的是,Python 的“名称改写”只是约定,并不是真正的访问控制。也就是说,外部仍然可以访问这些属性或方法,只是我们通过命名方式来提醒开发者哪些是“私有”的,哪些是“保护”的。接下来我们就来看具体的访问控制方式。
无下划线前缀(公有权限)
Python 中默认定义的属性和方法,都是公有的方法。无论是在类外,还是在派生的子类中,都可以进行访问,类似其它语言中的 public 修饰符的作用。
class A(object):
def __init__(self):
# 公有属性
self.a = 10
# 公有方法
def show(self):
# 在类中使用公有属性
print(f"A: {self.a}")
obj = A()
# 在类外使用公有属性
print(obj.a)
# 在类外使用公有方法
obj.show()
首先,我们有最简单的情况:公有权限。在 Python 中,默认定义的属性和方法都是公有的,也就是你可以在类外部随意访问和修改它们。比如,我们有一个简单的类 A,里面有一个公有属性 a,和一个公有方法 show:这里的 a 和 show 都可以在类外部直接访问,和其他语言中的 public 修饰符一样,不受限制。
_ 单下划线前缀(保护权限)
Python 在类中使用 单下划线前缀实现其它语言中 protected 保护权限的功能,在属性或方法(包括类属性和类方法,作用相同)前添加一个单下划线,该属性或方法,在当前类中可以访问,在类外理论上不可访问(使用时不提示,但写出来程序可以运行,但有警告),在通过继承派生的子类中可以访问(继承在后面讲解)。
class A(object):
def __init__(self):
# 公有属性
self.a = 10
# 保护属性
self._b = 20
# 公有方法
def show(self):
# 在类中使用公有属性
print(f"A: {self.a}")
# 在类中使用保护属性
print(f"B: {self._b}")
# 在类中使用保护权限的方法
self._display()
# 保护权限的方法
def _display(self):
print(f"B: {self._b}")
obj = A()
# 在类外使用公有属性
print(obj.a)
# 在类外无法使用保护仅限的属性(不建议这样使用)
print(obj._b)
# print(obj.__c)
# 在类外使用公有方法
obj.show()
# 在类外无法使用保护权限的方法(不建议这样使用)
obj._display()
接下来我们看一下保护权限。在 Python 中,如果我们在属性或方法前加一个单下划线,就表示它是“受保护的”。这并不是严格的私有,只是一个约定,表示不应该在类外部访问这些属性和方法。实际上,仍然可以访问它们,但 Python 会提醒你这不是一个“好的做法”。在这个例子中,下划线b 是保护属性,它在类外理论上不应该直接访问,尽管 Python 没有禁止它的访问,但这种访问方式是不推荐的。
__ 双下划线前缀(私有属性)
Python 在类中使用 双下划线前缀 实现其它语言中 private 私有权限的功能。
在属性或方法(包括类属性和类方法,作用相同)前添加一个双下划线,该属性或方法,只能在当前类中可以访问,在类外任何位置不可访问(只是理论上不可访问,通过某些方式,还是可以在类外访问,不建议这样使用)。
class A(object):
def __init__(self):
# 公有属性
self.a = 10
# 保护属性
self._b = 20
# 私有属性
self.__c = 30
# 公有方法
def show(self):
# 在类中使用公有属性
print(f"A: {self.a}")
# 在类中使用保护属性
print(f"B: {self._b}")
# 在类中使用私有属性
print(f"C: {self.__c}")
# 在类中使用保护权限的方法
self._display()
# 在类中使用私有方法
self.__info()
# 保护权限的方法
def _display(self):
print(f"B: {self._b}")
# 私有权限的方法
def __info(self):
# 在类中使用私有属性
print(self.__c)
obj = A()
# 在类外使用公有属性
print(obj.a)
# 在类外无法使用保护仅限的属性(不建议这样使用)
print(obj._b)
# 在类外使用私有属性,访问失败
# print(obj.__c)
# 在类外使用公有方法
obj.show()
# 在类外无法使用保护权限的方法(不建议这样使用)
obj._display()
# 在类外访问私有方法,访问失败
# obj.__info()
接下来是私有属性,即我们在属性或方法名前加上双下划线。这就表示它是私有的,只有在类的内部可以访问。在类外部,任何地方都不允许访问这些属性或方法。虽然 Python 通过双下划线“私有化”了属性,但实际上它通过名称改写的方式来实现的,所以类外仍然可以通过一些技巧访问这些私有属性,但不推荐这样做。这里我们就不讨论具体访问方式了。
双下划线前缀与后缀
在 Python 中还有一种同时具有前后双下划线的变量或方法,这些方法是 Python 中的魔法属性或魔法方法,这些属性或方法名被赋予了特殊的作用。
比如这些都是魔法方法:
- 初始化方法
__init__() - 对象描述方法
__str__() - 迭代器方法
__iter__() - 迭代器方法
__next__()
除了前面的三种权限控制,Python 中还有一种带有双下划线前后缀的特殊变量或方法。这些方法或属性有特殊的含义,通常被称为魔法方法或魔法属性。比如:init() 是初始化方法,用来在创建对象时进行初始化。str() 是描述对象的方法,通常用来返回对象的字符串表示。iter() 和 next() 是迭代器的方法。这些魔法方法是 Python 自带的一些特殊方法,用来定义对象的特殊行为,虽然它们是“魔法的”,但本质上你还是可以像普通方法一样访问和使用它们。
总结
- 访问控制
- 无下划线前缀(公有权限)
_单下划线前缀(保护权限)__双下划线前缀(私有属性)- 双下划线前缀与后缀
总结一下,封装和访问控制的几个关键点:公有属性:没有下划线,类外可以自由访问。保护属性:一个下划线前缀,提醒外部不要访问,但可以访问。私有属性:双下划线前缀,类外不能直接访问,虽然通过名称改写技术可以访问,但不推荐这么做。魔法方法:有双下划线前后缀,通常用来实现对象的特殊行为。通过封装和合理的访问控制,我们可以保护数据的安全,增强代码的可维护性和模块化,避免外部无意中修改对象的内部状态。