python继承、多态---子类和父类、super()、派生、抽象类、多继承、多态特性

218 阅读11分钟

目录

一、继承特性

1.什么是继承?

2.继承中子类和父类的概念

3.继承的作用

4. 查看继承的父类 

 5.方法的复写

6.super()

 7.__init__()方法 

8. 派生属性

9.私有属性私有方法在继承中的表现

10.抽象类 

 二、多继承

1、语法

3、继承原理(钻石继承)

4、多继承中super本质

三、多态特性


一、继承特性

1.什么是继承?

继承就是让类和类之间产生父子关系,子类可以拥有父类的静态属性和方法。

继承就是可以获取另外一个类中的静态属性和普通方法。(并非所有成员)

在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

注意:python中的继承分为:单继承和多继承。

2.继承中子类和父类的概念

父类:用于被继承的类,称之为父类,也叫做基类,或者超类。

子类:继承其他类的类,称之为子类,也叫做派生类。

3.继承的作用

提高代码的重用率

练习1:创建Dog类和Cat类,分别设置name,age属性和定义eat()、sleep()方法。

并且Dog类在定义一个look_door()方法,Cat类定义climb_tree()方法。

dog = Dog('京东狗', 8)
dog.eat() # 吃饭
dog.sleep() # 睡觉
dog.look_door() # 看门狗

cat = Cat('天猫', 13)
cat.eat() # 吃饭 
cat.sleep() # 睡觉
cat.climb_tree() # 爬树

注意:我们发现Cat了和Dog类中有大量的重复代码。

练习2:使用继承方式,实现Dog类和Cat类重复代码的重用。

class Animal(object):
    type = '动物'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print('吃饭')

    def sleep(self):
        print('睡觉')


class Dog(Animal):

    def look_door(self):
        print('看门狗')

class Cat:

    def climb_tree(self):
        print('爬树')
dog = Dog('京东狗', 8)
dog.eat() # 吃饭
dog.sleep() # 睡觉
dog.look_door() # 看门狗
print(dog.type) # 动物
cat = Cat('天猫', 13)
cat.eat() # 吃饭 
cat.sleep() # 睡觉
cat.climb_tree() # 爬树

4. 查看继承的父类 

格式:类名.__bases__

注意:

(1).python3中如果一个类没有继承任何类,默认继承object类。我们管这种类叫做新式类。

(2).object类,是python中的祖宗,所有的类都是从object类中继承下来的。

 练习1:查看Cat类和Animal类继承的父类。

print(Dog.__bases__)
print(Animal.__bases__)
'''
(<class '__main__.Animal'>,)
(<class 'object'>,)
'''

 5.方法的复写

子类中定义了和父类中相同的方法,我们叫做方法的复写(派生方法)。实例对象调用此方法 的时候就会调用自己类中的方法了。

练习1:定义一个Pig类继承上面的Animal类,重写父类中的eat方法

class Pig(Animal):

    def eat(self):
        print('吃猪饲料')

pig = Pig('荷兰猪', 3)
pig.eat()  # 吃猪饲料

6.super()

子类和父类有相同的方法,如果子类想调用父类相同的的方法。可以使用super()方法。

在python3中,子类执行父类的方法也可以直接用super方法 --->super()

super默认省略了两个参数 第一个参数是类名,第二个参数是self。两个参数可以省略不传递例如 super(Student,self)

super()还可以从类的外部使用 需要传递类名(本类的名称)和对象名

例如 super(Student,student)

格式:

父类类名.方法名称(self) 或者 super().方法名称()或者super(本类类名,对象名)

练习1:子类调用自己的方法的时候同时调用父类的方法

class Dog(Animal):

    def sleep(self):
        # 方法一 父类.方法名(对象)
        # Animal.eat(self)
        # 方法二 super(子类,对象名).方法名()
        super(Dog,dog).sleep()
        print('去狗窝')

    def look_door(self):
        print('看门狗')

dog = Dog('哈巴狗',23)
dog.sleep() 
'''
睡觉
去狗窝
'''

练习2:在类的外部调用super() 

# 类外部调用super()
dog = Dog('哈巴狗', 23)
super(Dog, dog).sleep() # super(子类,对象名).方法名()
dog.sleep()
'''
睡觉
去狗窝
'''

 7.__init__()方法 

- 子类继承父类,如果子类不复写父类的__init__()方法,

   创建子类对象的时候会自动调用父类__init__()方法。

 - 子类继承父类,如果子类复写了父类的__init__()方法,

   创建的子类对象的时候不会再调用父类的__init__() 方法。

 - 注意:python要求复写父类的__init__()方法时,需要调用父类的__init__()

   因为存在隐患,例如父类的初始化方法有参数,子类初始化无参数,子类再调用父类的参数的时候就会报错。

class Perpon:
    def __init__(self,name):
        print('person...')
        self.name = name


class Student(Perpon):
    def __init__(self):
        print('student...')


student = Student()
print(student.name) # 报错
# 如果子类不复写父类的__init__()方法,就默认用父类的
# 如果复写,就值只用自己的,不建议使用
class Perpon:
    def __init__(self):
        print('person...')


class Student(Perpon):
    def __init__(self):
        # super().__init__()
        print('student...')


student = Student() # student...

8. 派生属性

属性的覆盖(派生属性):子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了(属性的覆盖)。

# 派生属性: 子类中自己添加的新属性
# 属性的覆盖: 子类和父类有相同属性,调用自己的
class Perpon:
    num = 20

    def __init__(self, name):
        print('person...')


class Student(Perpon):
    num = 10  # 把父类中的20覆盖

    def __init__(self, name, age):  # age 为派生属性
        super().__init__(name)
        self.name = name
        self.age = age
        print('student...')

    def study(self):
        print(super().num)
        pass


student = Student('赵四', 23)
print(student.name) # person...
print(student.age)
print(student.num)
student.study()
'''
person...
student...
赵四
23
10
20
'''

9.私有属性私有方法在继承中的表现

父类中的私有方法和私有属性都是不能被子类继承下来的。

练习1:测试父类中的私有属性和私有方法是否能被继承下来

class Perpon:
    num = 20
    __num1 = 12

    def __test1(self):
        print('__test1....')

    def test2(self):
        print('test2...')

class Student(Perpon):

    def test(self):
        print('num...')
        print(self.num)
        # print(Student.__num1)
        self.test2()
        # self.__test1()

student = Student()
student.test()
student.test2()
# student.__test1() # 报错
'''
num...
20
test2...
test2...
''' 

10.抽象类 

之前我们定义了Person类实现了eat()、drink()方法,

每种人都会吃喝但是吃喝的地点不同,如果实现了方法体就浪费了。因此我们可以只定义eat()方法,不实现方法体,这种形式我们可以将方法定义为抽象方法,具有抽象方法的类就叫做抽象类。

抽象类是一个特殊的类,只能被继承,不能实例化,抽象类中可以有抽象方法和普通方法。

  1. 从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,
  2. 该类不能被实例化,只能被继承,且子类必须实现抽象方法
  3. 抽象类中可以定义普通的方法。
  4. 抽象类也是定义规范。
  5. 使用抽象类,子类一般都是单继承。

1.定义抽象类

定义抽象类需要导入 abc模块。from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta):

或者:

class Subject:
    # 定义为抽象类
    __metaclass__ = ABCMeta

2.定义抽象方法 

抽象方法:只定义方法,不具体实现方法体。

在定义抽象方法时需要在前面加入:@abstractmethod

抽象方法不包含任何可实现的代码,因此其函数体通常使用pass。

# 抽象方法
    @abstractmethod  # 子类必须实现
    def eat(self): pass

    @abstractmethod
    def sleep(self): pass

注意:子类继承了抽象类父类,子类必须实现父类的抽象方法。

练习1:定义一个Animal抽象类,定义eat()、sleep()抽象方法,定义两个子类

继承Animal抽象类。

class Animal(metaclass=ABCMeta):

    # 抽象方法
    @abstractmethod  # 子类必须实现
    def eat(self): pass

    @abstractmethod
    def sleep(self): pass

    # 定义普通的方法
    def play(self):
        print('溜达...')


class Dog(Animal):

    def eat(self):
        print('吃骨头。。。')

    def sleep(self):
        print('默默地看门...')


dog = Dog()
dog.sleep() # 默默地看门...
dog.play() # 溜达...

 二、多继承

一个子类可以继承多个父类,就是多继承,并且拥有所有父类的属性和方法。

例如 孩子会继承自己的父亲和母亲的特征。

1、语法

class 子类名(父类名1,父类名2…) : pass

class A(object):
    num_a = 1

    def test1(self):
        print('A--test1')

    def test2(self):
        print('A--test2')


class B(object):
    num_b = 2

    def test3(self):
        print('B--test3')

    def test4(self):
        print('B--test4')


class C(A, B):
    def test5(self):
        print('C--test5')

c = C()
c.test1() # A--test1
c.test2() # A--test2
c.test3() # B--test3
c.test4() # B--test4
print(c.num_a) # 1 
print(c.num_b) # 2
c.test5() # C--test5

2、多继承注意事项

如果子类和父类有相同的方法,就会调用子类中的方法。

如果不同的父类中存在着相同的方法名称,子类对象调用的时候会调用哪个父类中的方法呢? Python会根据 MRO(method resolution order) 方法解析顺序列表进行查找。

提示:开发时,需要避免这种容易产生混淆的情况!--如果父类之间存在同名的属性和方法,应尽量避免使用多继承。

3、继承原理(钻石继承)

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

print(C.mro()) # 查看查找顺序
输出:[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查

2.多个父类会根据它们在列表中的顺序被检查

3.如果对下一个类存在两个合法的选择,选择第一个父类

class A(object):
    def test(self):
        print('A--test')


class B(A):
    def test(self):
        print('B--test')


class C(A):
    def test(self):
        print('C--test')


class D(B, C):
    pass


d = D()
d.test()
print(D.mro()) 
'''
B--test
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
'''

顺序: 

注意:D类有两个选择,默认选择B类执行。

在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法广度优先,定义类时需要继承object,这样的类称为新式类,否则为旧式类

只有在python3中才有 __mro__ 和 mor() 方法。

4、多继承中super本质

不是直接查找父类,而是根据调用节点的广度优先顺序执行的。

练习1:创建A、B、C、D类,D类继承B,C类,B类继承A类,C类继承A类。

在每个方法中都调用super().func()方法,查看执行顺序。

class A(object):
    def test(self):
        print('A--test')


class B(A):
    def test(self):
        super().test()

        print('B--test')


class C(A):
    def test(self):
        super().test()

        print('C--test')


class D(B, C):
    def test(self):
        super().test()
        print('D--test')

d = D()
d.test()
'''
A--test
C--test
B--test
D--test
'''

三、多态特性

多态就是不同子类对象调用父类的方法产生不同的结果。

练习2:使用不同的支付工具给商店支付钱。

# 使用不同的支付方式给商店付钱
import abc


class Pay(object):
    @abc.abstractmethod
    def pay(self, money): pass


class Alipay(Pay):
    def pay(self, money):
        print('支付宝到账{}'.format(money))


class ApplePay(Pay):
    def pay(self, money):
        print('苹果支付{}'.format(money))


class Person(object):
    def consumption(self, pay, money):
        pay.pay(money)


alipay = Alipay()
person = Person()
person.consumption(alipay, 1000) # 支付宝到账1000