9.6 继承和私有性
9.6.1 继承
面向对象编程的一大优点是对代码的重用(Reuse),重用的一种实现方法就是通过继承(Inheritance)机制。继承最好是想象成在类之间实现类型与子类型(Type and Subtype)关系的工具。
在之前的例子中,学校里的老师和学生可以抽象成两个类,有一些特征是他们都具有的,例如姓名、年龄和性别等。另外一些特征是他们各自独有的,例如教师的薪水、课程与假期等,学生的成绩和学费等。
学生类
属性:姓名、性别、年龄、学历、手机号码、个人账户余额等
功能:提出报名,缴费
教师类
属性;姓名、性别、年龄、学历、手机号码、员工编号等
功能:登记报名信息,分配教室等
如果为老师和学生创建两个独立的类,并分别对它们进行处理,增添上面所说的共有特征就意味着将其添加进两个独立的类,这很快就会使程序变得笨重,修改起来也容易遗漏从而产生错误。
更好的方法是创建一个公共类叫做SchoolMember,然后让老师类和学生类从这个类中继承(Inherit),也就是说他们将成为这一类型(类)的子类型,就可以向这些子类型中添加某些该类独有的特征。
SchoolMember类
属性:姓名、性别、年龄等
功能:活着
这种方法有诸多优点。如果增加或修改了SchoolMember的任何功能,它将自动反映在子类型中。举个例子,可以通过简单地向SchoolMember类进行操作,来为所有老师与学生添加一条新的ID卡字段。而对某一子类型作出的改动并不会影响到其它子类型。
同时还需要注意的是重用父类的代码,但不需要再在其它类中重复它们,当使用独立类型时才会必要地重复这些代码。
在上文设想的情况中,SchoolMember类会被称作基类(Base Class)或是超类(Superclass)或是父类。Teacher和Student类会被称作派生类(Derived Classes)或是子类(Subclass)。
Python中使用继承,在定义类时需要在类后面跟一个包含基类名称的元组。如果继承元组(Inheritance Tuple)中有超过一个类,这种情况就会被称作多重继承(Multiple Inheritance)。
class SchoolMember:
""" 基类,学校成员类,代表任何学校里的成员。 """
def __init__(self, name, age):
self.name = name
self.age = age
print('(初始化 SchoolMember: {})'.format(self.name))
class Teacher(SchoolMember):
""" 子类,教师类,从SchoolMember继承 """
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age)
self.salary = salary
print('(初始化 Teacher: {})'.format(self.name))
基类的__init__方法是通过self变量被显式调用的,因此可以初始化对象的基类部分。
因为在Teacher和Student子类中定义了__init__方法,Python不会自动调用基类SchoolMember的构造函数,必须自己显式地调用它。
相反,如果没有在一个子类中定义一个__init__方法,Python将会自动调用基类的构造函数。
这时可以通过在方法名前面加上基类名作为前缀,再传入self和其余变量,来调用基类的方法。
Python有两个内置函数可被用于继承机制:
使用 isinstance() 来检查一个实例的类型。例如:isinstance(obj, int),仅会在 obj 为 int 或某个派生自 int 的类时为 True。
使用 issubclass() 来检查类的继承关系。例如:issubclass(bool, int) 为 True,因为 bool 是 int 的子类。但是,issubclass(float, int) 为 False,因为 float 不是 int 的子类。
9.5-继承.py
# 继承
class SchoolMember:
""" 基类,学校成员类,代表任何学校里的成员。 """
def __init__(self, name, age):
self.name = name
self.age = age
print('(初始化 SchoolMember: {})'.format(self.name))
def tell(self):
""" 输出对象信息 """
print('Name:{}\tAge:{}\t'.format(self.name, self.age), end='\t')
class Teacher(SchoolMember):
""" 子类,教师类,从SchoolMember继承 """
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age)
self.salary = salary
print('(初始化 Teacher: {})'.format(self.name))
def tell(self):
SchoolMember.tell(self)
print('Salary: {}'.format(self.salary))
class Student(SchoolMember):
""" 子类,学生类,从SchoolMember继承 """
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age)
self.marks = marks
print('(初始化 Student: {})'.format(self.name))
def tell(self):
SchoolMember.tell(self)
print('Marks: {}'.format(self.marks))
print('初始化过程:')
t = Teacher('张老师', 40, 10000)
s = Student('李四', 25, 75)
print('\n对象调用方法:')
# 子类对象调用各自子类方法
t.tell()
s.tell()
print('\n检查实例类型:isinstance')
print('isinstance(t, Teacher):', isinstance(t, Teacher))
print('isinstance(t, SchoolMember):', isinstance(t, SchoolMember))
print('\n检查类的继承关系:issubclass')
print('issubclass(Student, SchoolMember):', issubclass(Student, SchoolMember))
print('issubclass(Student, Teacher):', issubclass(Student, Teacher))
运行结果为:
初始化过程:
(初始化 SchoolMember: 张老师)
(初始化 Teacher: 张老师)
(初始化 SchoolMember: 李四)
(初始化 Student: 李四)
对象调用方法:
Name:张老师 Age:40 Salary: 10000
Name:李四 Age:25 Marks: 75
检查实例类型:isinstance
isinstance(t, Teacher): True
isinstance(t, SchoolMember): True
检查类的继承关系:issubclass
issubclass(Student, SchoolMember): True
issubclass(Student, Teacher): False
9.6.2 成员私有性
和有的面向对象编程语言不同,Python没有关键字来标明变量的私有化情况,Python使用下划线来进行一定的私有化表示。
例如,有一个变量原本的标识符为xx,则:
xx:公有变量。
__xx:双前置下划线,私有化变量,避免与子类中的属性命名冲突,无法在外部直接访问,使用 _Class__object可以访问。
一些其他用法如下:
_xx:单前置下划线,常用于模块中,在一个模块中以单下划线开头的变量和函数被默认当作内部函数,使用from somemodule import * 时不会被导入。
xx_:单后置下划线,没有特别的含义,常用于避免与Python关键词的冲突。
__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__
例如:test.py
aaa = 111
_bbb = 222
__ccc = 333
class Student:
aa = 11
_bb = 22
__cc = 33
def __init__(self, a, b, c):
self.a = a
self._b = b
self.__c = c
在另一文件中引入,test1.py
import sys
import test
s = test.Student(1, 2, 3)
try:
print(s.a)
print(s._b)
print(s.__c)
except (AttributeError, NameError):
print(sys.exc_info()[0])
try:
print(test.Student.aa)
print(test.Student._bb)
print(test.Student.__cc)
except (AttributeError, NameError):
print(sys.exc_info()[0])
try:
print(test.aaa)
print(test._bbb)
print(test.__ccc)
except (AttributeError, NameError):
print(sys.exc_info()[0])
执行结果为:
1
2
<class 'AttributeError'>
11
22
<class 'AttributeError'>
111
222
333
如果采用 from ... import * 方式引入模块,test1.py
import sys
from test import *
s = Student(1, 2, 3)
try:
print(s.a)
print(s._b)
print(s.__c)
except (AttributeError, NameError):
print(sys.exc_info()[0])
try:
print(Student.aa)
print(Student._bb)
print(Student.__cc)
except (AttributeError, NameError):
print(sys.exc_info()[0])
try:
print(aaa)
print(_bbb)
except (AttributeError, NameError):
print(sys.exc_info()[0])
try:
print(__ccc)
except (AttributeError, NameError):
print(sys.exc_info()[0])
执行结果为:
1
2
<class 'AttributeError'>
11
22
<class 'AttributeError'>
111
<class 'NameError'>
<class 'NameError'>