Python中继承的语法是通过类名后的括号内增加父类名实现的:
class Father:
pass
class Son(Father):
# 继承于Father
pass当然,子类会直接继承父类的所有属性和方法:
Father.print_func = print
# 动态绑定方法
s = Son()
s.print_func('hi') # hi子类也能够重写父类的方法:
class Father:
def print_func(self, content):
print(content)
class Son(Father):
def print_func(self, content):
print('hi')
f = Father()
s = Son()
f.print_func('hello') # hello
s.print_func('hello') # hi查看子类的所有父类,可以直接读取__bases__属性:
print(Son.__bases__)
# (<class '__main__.Father'>,)现在来看一个问题:
class Father:
def __init__(self, age):
self.age = age
class Son(Father):
def __init__(self, height):
self.height = height
s = Son(100)
print(s.age)
# AttributeError: 'Son' object has no attribute 'age'很明显,Son覆盖了Father的初始化函数,age没有初始化,自然无法访问。那在Son里要怎么给Father初始化呢?很简单,直接调用Father的__init__方法:
class Son(Father):
def __init__(self, height, age):
Father.__init__(self, age)
self.height = height
s = Son(height=100, age=10)
print(s.age) # 10这又带来了另一个问题,如果一个Father有一百个子类,突然有一天,Father类改名叫Farther了,那么所有的子类都要修改初始化处的名字。更一般的,如果子类使用了大量的父类的方法,每个方法都要去修改Father这个名字,着实有些困难。所以,Python提供了一个更好的选择,利用super:
class Son(Father):
def __init__(self, height, age):
super(Son, self).__init__(age=age)
s = Son(height=100, age=10)
print(s.age) # 10在Python 3中,super不必加任何参数,直接调用super().__init__即可。在后续多重继承文章中会详细解释super本身及参数的意义。在单继承中,只要知道,利用super可以调用到父类的方法即可:
class Father:
def print_func(self):
print('I am father')
class Son(Father):
def print_func(self):
super().print_func()
print('I am son')
s = Son()
s.print_func()
# I am father
# I am son但是切记,super的意义并不是用于调用父类方法。super也存在一些问题。所以,当你能很明确地确定继承结构,并且很明确地确定继承结构基本不变,你应当直接用父类名调用父类方法,除非你很明确地清楚你在用super作什么。
继续来通过例子看Python中类的问题:
class Father:
def print_age(self):
print('My age: {}'.format(self.age))
class Son(Father):
def __init__(self, age):
self.age = age
f = Father()
f.print_age()
# ???
s = Son(10)
s.print_age()
# ???上述两个调用的结果是怎样的?
# AttributeError: 'Father' object has no attribute 'age'
# My age: 10很奇怪,父类的方法为什么能直接打出子类的属性??
答案就在于self。我们在一个更复杂的例子中看一下:
class Father:
def print_age(self):
print('My age: {}'.format(self.age))
class Mother:
pass
class Uncle:
def print_age(self):
print('Uncle\'s age: {}'.format(self.age))
class Son(Mother, Father, Uncle):
def __init__(self, age):
self.age = age
s = Son(10)
s.print_age()
# My age: 10可以看到,上例中Son继承自三个父类(多重继承在后续文章中详细介绍)。print_age仍旧找到了Father类中。它的内部调用情况是这样的:
for base_class in Son.__bases__:
print(base_class)
# 这里打印只是为了方便查看
if hasattr(
base_class,
'print_age'
):
base_class.print_age(s)
break
# <class '__main__.Mother'>
# <class '__main__.Father'>
# My age: 10Python先在Son里查找方法print_age,没有找到,之后便在Son的所有父类中依次寻找。在Mother类中什么都没找到,继续在Father类中寻找。hasattr方法可以判断一个对象中是否有某个方法。在Father中找到了print_age,然后以Son的实例s作为参数调用Father的print_age,这样Father.print_age的参数self则变成了s,所以,打印self.age即是打印s.age。
一旦找到了并完成调用后,即break掉该循环,不再从后续父类中再做查找。
如果你熟悉C++的面向对象编程,你应该发现Python类的方法都是虚函数(virtual),因为你可以从父类通过self访问到子类的方法。
class Father:
def print_name(self):
print('Father')
def who(self):
self.print_name()
class Son(Father):
def print_name(self):
print('Son')
s = Son()
s.who()
# 结果是什么?不再解释。当然Python中并不存在虚函数这种说法,仅仅是做一种类比。也希望大家在学习使用Python的时候尽量以Python的思路来思考,而不要以其他语言的思路来揣测Python的行为。
下面来看一下Son对象的类型:
# 1
print(isinstance(s, Son)) # True
# 2
print(isinstance(s, Father)) # True
# 3
print(isinstance(s, type)) # False
# 4
print(isinstance(s, object)) # True
# 5
print(isinstance(Son, type)) # True
# 6
print(isinstance(Son, object)) # True
# 7
print(issubclass(Son, type)) # False
# 8
print(issubclass(Son, object)) # True
# 9
print(isinstance(Father, type)) # True
# 10
print(type(Father)) # <class 'type'>
# 11
print(Father.__bases__)
# (<class 'object'>,)在这篇文章中解释了(注:issubclass示例有误),isinstance(a, b)查询a是否是b的实例(对象),issubclass(a, b)查询a是否是b的子类,type(a)返回a的类型
我们按顺序一个个解释一下:
s是Son的实例,isinstance(s, Son)自然是True;Son是Father的子类,所以Son的实例s自然也是Father的实例,大家都是同一血缘的;- 如果你仔细阅读了一切皆对象——Python面向对象(二),你应该清楚
type是一切类的类(或者叫类型),而不是一切实例的类!所以实例s并不是type的实例!类Son和类Father才是type的实例!请看# 5和# 9; object是什么?之前说过,Python一切皆对象,任何东西都能找到它的类型。而任何类型的最终源头是type。实际上,任何的类型都是从一个父类(或者叫基类)继承过来的,而父类的最终源头便是object。请看# 8和# 11;- 在3解释过了;
object是一切类最终的基类,一切自然也包括type:
print(type.__bases__)
# (<class 'object'>,)而object再无基类:
print(object.__bases__)
# ()既然object是type的父类,而Son是type的实例,那么Son自然是object的实例,类比于s即时Son的实例也是Son的父类Father的实例;
-
type不是父类,而是类型!object才是父类! -
在4解释过了;
-
在3解释过了;
-
在系列的上一篇中解释了;
-
在4解释过了;