首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python进阶教程笔记(二)类的继承、多态

Python进阶教程笔记(二)类的继承、多态

作者头像
Lemon黄
发布2020-10-30 11:42:23
4440
发布2020-10-30 11:42:23
举报
文章被收录于专栏:Lemon黄Lemon黄

一、什么是继承

  • 程序设计的范式
  • 抽象并建立对象模型
  • 程序是不同对象相互调用的逻辑

二、继承的优点

  • 新类不需要重头编写
  • 继承父类所有的属性、功能
  • 子类只需要实现缺少的新功能

三、继承类

对人类的抽象可以定义为Person类,而学生、老师等,也都是人类,所以,在Python当中,如果定义学生Student的类,可以继承Person类。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

接着定义Student类,在定义Student类的时候,由于继承了Person类,所以Student类自动拥有name、gender属性,因此,在定义Student类的时候,只需要把额外的属性加上即可。

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

student = Student('Alice', 'girl', 100)
print(student.name) # ==> Alice
print(student.gender) # ==> girl
print(student.score) # ==> 100

在定义继承类的时候,有几点是需要注意的:

  1. class Student()定义的时候,需要在括号内写明继承的类Person
  2. __init__()方法,需要调用super(Student, self).__init__(name, gender),来初始化从父类继承过来的属性

四、判断类型

随着我们学习步伐的前进,我们的程序会出现越来越多的类型,有我们自己定义的类,也有Python自有的str、list、dict等,他们的本质都是都是Python中的一种数据类型,这时有必要去判断数据的类型,通过函数isinstance()可以判断一个变量的类型。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

当我们拿到变量 p、s、t 时,可以使用 isinstance 判断类型:

>>> isinstance(p, Person)
True # p是Person类型
>>> isinstance(p, Student)
False # p不是Student类型
>>> isinstance(p, Teacher)
False # p不是Teacher类型

这说明在继承链上,一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法。 我们再考察 s:

>>> isinstance(s, Person)
True # s是Person类型
>>> isinstance(s, Student)
True # s是Student类型
>>> isinstance(s, Teacher)
False # s不是Teacher类型

s 是Student类型,不是Teacher类型,这很容易理解。但是,s 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 s 看成Person的实例也是可以的。

这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。

isinstance也可以用于Python自有数据类型的判断。

s = 'this is a string.'
n = 10
isinstance(s, int) # ==> False
isinstance(n, str) # ==> False

五、多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个who() 方法:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def who(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def who(self):
        return 'I am a Teacher, my name is %s' % self.name

接着,我们分别把不同类型的who()函数结果打印出来:

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

运行结果:

I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。从定义上来讲,Student和Teacher都拥有来自父类Person继承的who()方法,以及自己定义的who()方法。但是在实际调用的时候,会首先查找自身的定义,如果自身有定义,则优先使用自己定义的函数;如果没有定义,则顺着继承链向上找。

class Boss(Person):
    def __init__(self, name, gender,company):
        super(Boss, self).__init__(name, gender)
        self.company = company

b = Boss('Bob', 'Male', 'Alibaba')
b.who() # ==> I am a Person, my name is Bob

在Boss的定义类,没有定义who方法,所以会顺着继承链向上找到父类的who方法并且调用。

Python中的多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。多重继承和单继承没有特别大的差异,只是在括号内加入多个需要继承的类的名字即可。

class A(object):
    def __init__(self, a):
        print ('init A...')
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print ('init B...')

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print ('init C...')

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print ('init D...')

多重继承的继承链就不是一棵树了,它像这样:

从上图可知,A类被继承了连词,那么A的__init__()方法,是否会被调用两次呢?

d = D('d')
init A...
init C...
init B...
init D...

实践证明,在多重继承里,A虽然被继承了两次,但是__init__()的方法只调用一次。

多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有 多进程ForkingMixin 和 多线程ThreadingMixin两种。

要创建多进程模式的 TCPServer:

class MyTCPServer(TCPServer, ForkingMixin)
    pass

要创建多线程模式的 UDPServer:

class MyUDPServer(UDPServer, ThreadingMixin):
    pass

如果没有多重继承,要实现上述所有可能的组合需要 4x2=8 个子类。

六、获取对象信息

在前面,我们通过isinstance()方法,可以判断一个对象是否是某个类型,从某种意义上来讲,通过isinstance()方法,我们获取到了一个对象的一些信息,那有没有别的方法可以获取到对象更多的信息呢? 通过type()函数,可以获得变量的类型。

n = 1
s = 'this is a string'
type(n) # ==> <class 'int'>
type(s) # ==> <class 'str'>
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

p = Person('Alice', 'Female')
s = Student('Bob', 'Male', 100)
type(p) # ==> <class '__main__.Person'>
type(s) # ==> <class '__main__.Student'>

通过dir()方法,可以获取变量的所有属性:

>>> n = 1
>>> dir(n)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', ...]
>>> s = 'this is a string'
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', ...]

在dir列出的属性中,有很多是以下划线开头和结尾的,这些都是特殊的方法,称为内建方法,在后面,我们还会学习这些方法。 而对于自定义对象:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def who(self):
        return 'I am a Person, my name is {}'.format(self.name)

p = Person('Alice', 'Female')
dir(p)

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'who']

对于实例变量,dir()返回所有实例属性,包括__class__这类有特殊意义的属性。注意到方法who也是p的一个属性。

dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr( )函数了。

>>> getattr(p, 'name') # 获取name属性
'Alice'
>>> setattr(p, 'name', 'Adam') # 设置新的name属性
>>> s.name
'Adam'
>>> getattr(s, 'age') # 获取age属性,但是属性不存在,报错:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
>>> getattr(s, 'age', 20) # 获取age属性,如果属性不存在,就返回默认值20:
20
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Lemon黄 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是继承
  • 二、继承的优点
  • 三、继承类
  • 四、判断类型
  • 五、多态
    • Python中的多重继承
    • 六、获取对象信息
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档