一切皆对象——Python面向对象(三)

单一继承

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

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180531G22RPC00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券