本章学习链接如下:
Python 面向对象——1.基本概念
Python 面向对象——2.类与对象实例属性补充解释,self的作用等
Python 面向对象——3.实例方法,类方法与静态方法
Python 面向对象——4.继承
Python 面向对象——5.多态
1. 封装的基本概念
封装是将属性和方法结合在一起的机制,同时限制对对象内部实现的直接访问。在Python中,封装通常是通过定义类和使用私有变量(以双下划线开头)来实现的。封装的主要目的是隐藏对象的内部状态和复杂性,只暴露出一个清晰和简洁的接口供外部代码使用。
(1)私有变量
私有变量是一种访问限制机制,用于限制类外部对类内部属性或方法的直接访问。
命名约定:在Python中,私有变量通常以两个下划线(_ _)开头。例如:_ _my_variable
。那这是怎样做到的呢?源于Python中的名称重整机制。
(2)名称重整规则
-
如果属性名以双下划线开头,但不以下划线结尾(
_ _name
),那么在名称重整时,会将属性名中的双下划线替换为类名,然后再加上一个单下划线。例如,对于类MyClass
中的属性_ _my_variable
,重整后的名称将是_MyClass_ _my_variable
。 -
如果属性名以双下划线开头,并且以下划线结尾(
_ _my_variable_ _
),那么在名称重整时,会保留一个下划线在属性名的开头和结尾。例如,对于类MyClass
中的属性_ _my_variable_ _
,重整后的名称将是_MyClass_ _my_variable_ _
。
我们看下面这个代码示例,self._ _salary
是一个私有属性,它被声明为私有的,因为它的名称以双下划线开头。根据名称重整规则,这个属性在类外部的名称将变为 _Employee_ _salary。
self._ _top_secret()
是一个私有方法,同样因为以双下划线开头,它在类外部的名称将变为 _Employee_ _top_secret
。当我们尝试直接访问这些私有成员时,Python解释器会抛出一个 AttributeError
,因为它找不到这些属性或方法。然而,如果我们知道了名称重整后的名称,我们仍然可以访问到这些私有成员,但这违反了封装的原则,因此通常不推荐这样做。
请注意,尽管我们可以通过名称重整后的名称访问私有成员,但这并不是Python推崇的做法。更好的做法是通过类的公有接口(如方法)来访问或修改私有成员,以保持类的封装性。
class Employee:
def __init__(self, name):
self.name = name # 公开属性
self.__salary = 50000 # 私有属性,使用双下划线开头
def give_raise(self, amount):
self.__salary += amount
print(f"{self.name}'s new salary is: ${self.__salary}")
def __top_secret(self):
pass # 私有方法,使用双下划线开头
# 创建Employee类的实例
emp = Employee("Alice")
# 尝试直接访问私有属性(这将不会成功)
try:
print(emp.__salary) # 错误:__salary不是Employee的属性
except AttributeError as e:
print(e)
# 尝试调用私有方法(这也将不会成功)
try:
emp.__top_secret() # 错误:__top_secret不是Employee的方法
except AttributeError as e:
print(e)
# 使用公开方法访问私有属性
print(f"{emp.name}'s salary is: ${emp._Employee__salary}")
请注意,名称重整并不是真正的私有化,而是一种约定。它通过名称改写来减少属性被外部访问的可能性,但并不阻止有经验的程序员绕过这个机制。在Python中,如果你想要完全私有的属性,可以使用单下划线开头的属性名(_my_variable
),但这只是一种约定,表示该属性不应该被外部直接访问。
(3)try:
try
和except
语句用于异常处理。当你执行一个可能会抛出异常的代码段时,可以将该段代码放在try
块中。如果在try
块中的代码执行过程中发生了异常,程序的执行将不会立即停止,而是会跳转到except
块中,这允许你捕获并处理异常。
在这段代码示例中,try
块用于尝试访问Employee
类的私有属性_ _salary
和调用私有方法_ _top_secret
。根据Python的命名规则,以双下划线开头的属性和方法对外部是不可访问的,因为它们被视为私有的(尽管这不是一种真正的私有机制,只是一种命名约定)。
代码中的两个try
块尝试执行如下操作:
-
尝试直接访问
emp
实例的私有属性_ _salary
。由于_ _salary
是私有的,这个尝试会抛出AttributeError
,因为_ _salary
不是Employee
实例的公开属性。 -
尝试调用
emp
实例的私有方法_ _top_secret
。同样,由于_ _top_secret
是私有的,这个尝试也会抛出AttributeError
。
2.错误捕获
(1)背后的原理
except AttributeError as e:
print(e)
当AttributeError
被捕获时,变量e
是一个异常对象。这个对象包含了异常的详细信息,当你打印e
时,实际上是调用了该对象的__str__()
方法,它返回异常的描述性字符串。
当你打印一个异常对象时,背后实际上是调用了该对象的__str__()
方法。这是Python对象的内置特殊方法(也称为"dunder"方法,即双下划线前缀和后缀的方法),用于返回对象的字符串表示形式。即使在给定的类定义中没有显式地看到__str__()
方法,它也是隐式存在的,因为所有的Python对象默认继承自object
类,而object
类提供了一个默认的__str__()
实现。
异常类(如AttributeError
)通常会重写__str__()
方法,以返回一个描述错误的字符串。当你执行print(e)
,Python首先查找对象e
的__str__()
方法,并调用它来获取字符串表示,然后打印出来。
这是Python中所有异常对象的标准行为,所以即使你在类定义中没有看到显式的__str__()
方法,当你打印异常对象时,它也是可用的。
下面是一个简单的示例,展示如何自定义一个类并重写__str__()
方法:
在上面的代码中,我们创建了一个自定义异常MyError
,它继承自Python的基类Exception
。我们重写了__str__()
方法来返回一个自定义的错误信息。当异常被捕获并打印时,会显示我们提供的信息。
class MyError(Exception):
def __str__(self):
return "自定义错误信息"
try:
raise MyError()
except MyError as e:
print(e) # 输出: 自定义错误信息
(2)Exception
类
Python的Exception
类是所有内置和用户定义的异常的基类。它位于异常类继承层次结构的顶端,提供了处理异常时的默认行为。在Python中,异常是用于错误处理的一种机制,允许程序在发生错误时从代码的某个部分“跳出”,并提供一种方式来响应或恢复。