一、类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
在OOP(Object Oriented Programming,面向对象编程)程序设计中,当我们定义一个 class 的时候,可以从某个现有的 class 继承,通过继承创建的新类称为子类或派生类(Subclass),被继承的类称为基类、父类或超类(Base class、Super class)。
1、继承语法
class 派生类名(基类名)
...
2、在python继承中的一些特点:
- 如果子类需要父类的构造方法,就需显示调用父类的构造方法(即子类调用父类的构造方法重写子类的构造方法),或者不重写父类的构造方法;
- 在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数;
- Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
下面例子定义一个 Person 类,在 Person 类中定义属性变量 name 及 sex (姓名和性别);
定义一个方法 print_title(),当 sex 是 male 时,print man,当sex 是female时,print woman;
而我们编写 Child 类,完全可以继承 Person 类(Child 就是 Person);使用 class subclass_name(baseclass_name) 来表示继承。
代码如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): #Child 继承 Person
pass
Peter = Person("Peter","male") #创建父类Person的一个对象Peter
May = Child("May","female") #创建子类Child的一个对象May
print(May.name,May.sex,Peter.name,Peter.sex) #子类继承父类方法及属性
May.print_title() #调用父类的方法
Peter.print_title() #调用父类的方法
由此可见继承的最大好处是子类获得了父类的全部属性及功能。如上 Child 类可以直接使用父类的 print_title() 方法,实例化Child的时候,子类继承了父类的构造函数,就需要提供父类 Person 要求的两个属性变量 name 及 sex。
在继承关系中,如果一个实例的数据类型是某个子类,那它也可以被看做是父类(May 既是 Child 类又是 Person 类)。但是,反过来就不行(Peter 仅是 Person 类,而不是 Child 类)。
Python 与其他语言不同点在于,当我们定义一个 class 的时候,相当于定义了一种数据类型。我们定义的数据类型和 Python 自带的数据类型,比如 str、list、dict 没什么两样。
因此,我们可以使用 issubclass() 或者 isinstance() 方法来检测上面所说的关系。
- issubclass() — 布尔函数,用于检查类继承。判断一个类是否是另一个类的子类或者子孙类,语法:issubclass(sub,sup);
- isinstance(obj, Class) — 布尔函数,用于检查实例类型。如果 obj 是 Class 类的实例对象或者是一个 Class 子类的实例对象,则返回 True。
print(isinstance(May,Child)) # May是Child类型,True
print(isinstance(May,Person)) # May是Person类型,True
print(isinstance(Peter,Child)) # Peter是Child类型,False
print(isinstance(Peter,Person)) # Peter是Person类型,True
print(issubclass(Child,Person)) # Child类继承Person类,True
继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:
二、类的多态
下面代码在子类 Child 中重写 print_title() 方法:若为 male,print boy;若为 female,print girl
class Person():
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): #Child继承Person
def print_title(self): #修改父类的print_title()方法
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
May = Child("May","female") #创建Child的一个对象May
Peter = Person("Peter","male") #创建Person的一个对象Peter
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title() #调用子类的print_title()方法
Peter.print_title() #调用父类的print_title()方法
结果如下:
May female Peter male
girl
man
观察上面代码及其运行结果可以看出,当子类和父类都存在相同的 print_title
()
方法时,子类的 print_title()
覆盖了父类的 print_title()
,在代码运行时,总是会调用子类的 print_title()
。这样,我们就获得了继承的另一个好处:多态。多态的好处就是,当我们需要传入更多的子类,例如新增 Teenagers、Grownups 等时,我们只需要继承 Person 类型就可以了,而print_title() 方法既可以不重写(即使用Person的),也可以重写一个继承类特有的,这就是多态的意思。
对于一个变量,我们只需要知道它是 Person 类型,无需确切地知道它的子类型,就可以放心地调用 print_title
()
方法,而具体调用的 print_title()
方法是作用 Child、Teenagers还是 Grownups 对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种 Person 的子类时,只要确保新方法编写正确,不用管原来的代码是如何调用的,这就是著名的 "开闭" 原则:
- 对扩展开放(Open for extension):允许子类重写方法函数
- 对修改封闭(Closed for modification):不需要重写方法,直接继承父类方法函数
三、子类重写构造函数
子类可以没有构造函数,表示同父类构造一致;子类也可重写构造函数;下面代码在子类 Child 中新增两个属性变量:mother 和 father,我们可以构造如下(建议子类调用父类的构造方法,参见后续代码):
class Person():
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): #Child 继承 Person
def __init__(self,name,sex,mother,father): #子类Child重写构造方法
self.name = name
self.sex = sex
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
结果如下:
May female April June
若父类构造函数包含很多属性,子类仅需新增1、2个,会有不少冗余的代码,所以子类可对父类的构造方法进行调用,以减少代码量,代码如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): #Child 继承 Person
def __init__(self,name,sex,mother,father): #子类调用父类构造方法重写子类的构造方法
Person.__init__(self,name,sex) #在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
结果如下:
May female April June