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