本文主要记录Python中支持面向对象编程的特性、类的定义、封装、继承等概念。
1. 类的定义及实例化
面向对象编程是通过类来实现的,类提供了一种组合数据和操作的方法。Python中定义一个类的基本语法如下:
1 | class Student: # 创建了一个Student类 |
类支持两种操作:属性引用和实例化。
- 属性引用,使用的是Python中属性引用的标准语法,即
obj.var
。Student.school
和Student.printName
是有效的属性引用,分别返回一个字符串对象和一个函数对象。 - 实例化,类的实例化使用函数表示法,即
stu=Student('lihua')
,创建了类的实例并将其分配给局部变量stu
。类有一个__init__()
构造方法,该方法在类实例化时会自动调用。
2. 类的变量和方法
2.1 类变量和实例变量
- 类变量:类内部定义的变量,其属于类,可以通过类直接访问,所有实例都可以访问,类似于C++中的静态成员变量。
- 实例变量:以
self
开头定义的变量,self
代表类的实例,实例变量属于特定的实例。
NOTE:实例变量的访问优先级高于类变量,所以当实例变量和类变量同名时,优先访问实例变量,屏蔽对类变量的访问。
2.2 类方法、实例方法和静态方法
在类的内部,通过使用def
关键字来定义方法。在类内部定义的方法分为三种:实例方法、类方法和静态方法。
- 实例方法:类内部定义的没有装饰器且第一个参数为
self
的方法。可通过实例直接访问,如果要通过类访问,需要将实例作为参数传入。1
2stu.printName() # 实例访问,输出:lihua
Student.printName(stu) # 类访问,必须传入实例对象,输出:lihua - 类方法:类内部定义的以
@classmethod
装饰的方法,其第一个参数为cls
,表示将类对象本身传入方法。可以通过类直接访问,不需要实例化。类方法的一个主要用途是定义多个构造器来对类进行初始化。1
2Student.printGrade('grade 2019') # 可以通过类直接访问,输出:grade 2019
stu.printGrade('grade 2016') # 可以通过实例进行访问,输出:grade 2016 - 静态方法:类内部定义的以
@staticmethod
装饰的方法。实例和类都可对其进行直接访问,与类方法不同在于静态方法不需要传入参数cls
。
2.3 property使用
Python中property的作用有两个:
- 作为装饰器
@property
将类方法转为类变量1
stu.printName # 作为属性而不是方法进行访问,输出:lihua
- property重新实现setter和getter方法
2.4 属性和方法的访问限制
在Python中,类的访问权限有公开和私有两种:
- public:公开访问,正常的函数和变量名都是公开的,可以被外部访问
- private:私有访问权限,通过在属性名和方法名前面加双下划线
__
进行标识。私有化后的属性和函数只能在类的内部访问
3. 继承
Python中类的继承有单继承和多继承两种方式,派生类定义的一般格式如下:
1 | class DerevedClass(BaseClass1[,BaseClass2,BaseClass3]): |
3.1 继承实现的原理
对于定义的每一个类,Python会计算出一个方法解析顺序(MRO)列表,这个MRO列表是一个简单的线性顺序表,里面存储的是基类对象。MRO列表遵循如下准则:
- 派生类会先于基类被检查
- 多个基类会根据它们在列表中顺序被检查
- 如果基类有相同的方法名,子类中使用时未加指定,Python从左至右进行搜索基类中是否包含该方法,并选择第一个遇到的基类
3.2 派生类中调用基类的方法
在派生类中往往需要重用基类的方法,实现的方法有两种:
- 通过属性引用的方式,即
BaseClass.func()
- 通过
super()
方法,super()
依赖于继承,即使没有直接继承关系,super()
仍然按照MRO继续查找1
2
3
4
5
6
7
8
9
10class A:
def test(self):
super().test()
class B:
def test(self):
print('from class B')
class C(A,B):
pass
c = C()
c.test() # 输出:from class B