python-继承&派生&组合

继承&派生

什么是继承

继承指的是类与类之间的关系,是一种"父子关系",在Py中,继承是一种创建新类的方式

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

子类可以继承父类的特征属性和方法属性,所以说,继承的功能之一就是解决代码重用问题

单继承&多继承

单继承:子类只继承一个父类

多继承:子类继承多个父类

class ParentClassA: # 定义父类
    pass
class ParentClassB: # 定义父类
    pass
class SubClass1(ParentClassA):  # 单继承,定义子类时把父类名放在括号中即可
    pass
class SubClass2(ParentClassA, ParentClassB): # 多继承,继承时,多个父类用逗号隔开
    pass

查看继承的方法

类名.__base__ :只查看从左到右继承的第一个类

类名.__bases__ :查看所有继承的父类

print(SubClass1.__base__)  
print(SubClass2.__base__)
print(SubClass1.__bases__)
print(SubClass2.__bases__) 

输出结果:

<class 'main.ParentClassA'>
<class 'main.ParentClassA'>
(<class 'main.ParentClassA'>,)
(<class 'main.ParentClassA'>, <class 'main.ParentClassB'>)

经典类&新式类

在Py2中类分经典类和新式类,在Py3中所有的类同一默认为新式类

Py2中经典类:没有显示继承object类的类,以及该类的子类,都是经典类

Py2中新式类:显示的声明继承object类的类,以及该类的子类,都是新式类

Py3中无论是否继承object类,都默认继承object类

  • 如果没有指定基类,Py3会默认继承object类,object类提供常见方法(如,__str__)
ParentClassA.__bases__   # 打印 (<class 'object'>,)

先抽象再继承

抽象即抽取类似或者说比较像的部分,抽象的作用是划分类别(隔离关注点,降低复杂度)

继承是基于抽象的结果,通过编程语言去实现它,经历抽象这个过程,才能通过继承的方式去表达抽象的结果

抽象只是分析和设计的过程中,一个动作或一个技巧,通过抽象分析得到程序中的类

属性查找顺序

对象访问其属性时,先在对象本身的命名空间找,若找不到;再到其类的命名空间找,若找不到;再到父类中找,直到最顶级父类,如果都找不到,则报错。

分析:

class Foo:
    def f1(self):
        print('Foo.f1')
    def f2(self):
        print('Foo.f2')
        self.f1()
class Bar(Foo):
    def f1(self):
        print('Bar.f1')

b=Bar()
b.f2()

# 打印结果:
# Foo.f2
# Bar.f1
  • 对象b发起的访问其属性f2(),首先其本身没有,再在其类Bar中找,找不到;再到其父类Foo中查找,找到f2();此时即开始执行函数f2(),先打印"Foo.f2",再调用self.f1()。此时,self就是对象b,故执行self.f1()就等于是执行b.f1(),所以又开始现在b中找f1,找不到;再在b的类Bar中找,找到,于是打印"Bar.f1"

继承的实现原理

继承实现原理:定义一个类,python会计算出一个方法解析顺序(MRO)列表,这个列表就是一个简单的所有父类的线性顺序列表。通过 "类名.mro()" 查看,等同于调用 "类名.__mro__"

Py调用查找属性时:会在MRO列表上从左到右开始查找父类,直到找到第一个匹配这个属性的类为止。

属性查找:

  • 对于单继承的类,就是按照继承的一条直线来找;
  • 对于多继承的类,就按照MRO列表的顺序来找;

Py中,对于多继承的查找,按照MRO列表顺序查找。经典类和新式类是有区别的。

  • 经典类按照 深度优先的原则
  • 新式类按照 广度优先的原则

派生

子类可以定义自己属性,当与父类的属性相同时,不会影响父类

注意,一旦重新定义了子类的属性且与父类的重名时,子类及其对象调用该属性时,就以子类的为准

子类中,在新建与父类重名的方法属性时,可以既重用父类的那个方法属性,又加上自己的新功能。

此时,可以使用普通函数的调用方式,即 类名.func(),此时与调用普通函数无异,需要手动传参(包括self)

class Hero:
    def __init__(self,nickname,aggressivity,life_value): # 父类方法属性
        self.nickname=nickname
        self.aggressivity=aggressivity
        self.life_value=life_value

class Riven(Hero):
    camp='Noxus'
    def __init__(self,nickname,aggressivity,life_value,skin):
        Hero.__init__(self,nickname,aggressivity,life_value) # 调用父类功能
        self.skin=skin  # 新属性

r1=Riven('锐雯雯',57,200,'比基尼')  # 子类对象初始化

子类调用父类的方法

子类派生的新方法中,往往需要重用父类的方法,有两种实现方式:

方式一:指名道姓,即 父类名.父类方法()

  • 就是上面代码示例中的方式
  • 此方式不依赖继承关系;其实就是调用普通函数的,与继承无关,需要手动传参。
class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power) # "指名道姓" 调用父类方法
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        Vehicle.run(self)    # "指名道姓" 调用父类方法

方式二:super()

  • super().属性([参数])或 super(当前的类名,self).属性([参数])
  • super方式依赖继承关系;super在MRO列表
class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(self,name,speed,load,power)
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        super(Subway,self).run()  # 等同于 super().run()
  • 且就算没有继承关系,super依然会按照MRO列表继续往后查找

  • 基于谁的查找,遇到super后,接着MRO列表的顺序往后找,直到找到一个为止

    #A没有继承B,但是A内super会基于C.mro()继续往后找
    class A:
        def test(self):
            super().test()
    class B:
        def test(self):
            print('from B')
    class C(A,B):
        pass
    
    c=C()
    c.test() #打印结果:from B
    
    
    print(C.mro())
    #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

组合

组合的定义

组合指的是,一个类使用另一个类的对象作为自己的特征属性,即类的组合

组合反映的也是类与类之间的关系,是一种"有"的关系

当类与类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较方便

class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
    
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    def tell_info(self):
        return "%s-%s-%s" %(self.year, self.month, self.day)

stu1 = Student("小明", 10, "male")
d = Date(2010, 8, 8)
stu1.birthday = d
print(stu1.birthday.tell_info())

猜你喜欢

转载自www.cnblogs.com/liuxu2019/p/12115901.html