python 类的继承和派生 1 什么是继承 3 组合 5 接口和归一化设计 6 抽象类

     面向对象的编程带来的主要好处之一是代码的复用,实现这种复用的方法之一是通过继承机制。
     python中解决代码复用的方式:继承和组合。

     继承是一种创建类的方法,在python中,一个类可以继承来自一个或多个父类。原始类称为基类或超类。

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

查看继承:

>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

1.1 什么时候用继承

     假如已经有几个类,而类与类之间有共同的变量属性和函数属性,那就可以把这几个变量属性和函数属性提取出来作为基类的属性。而特殊的变量属性和函数属性,则在本类中定义,这样只需要继承这个基类,就可以访问基类的变量属性和函数属性。可以提高代码的可扩展性。

1.2 继承和抽象(先抽象再继承)

抽象即提取类似的部分。
基类就是抽象多个类共同的属性得到的一个类。

class Riven:
    camp='Noxus'
    def __init__(self,nickname,
                 script,
                 aggressivity=54,
                 life_value=414,
                 ):

        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
        self.script=script

    def attack(self,enemy):
        print(self.script)
        enemy.life_value -= self.aggressivity


class Garen:
    camp='Demacia'
    def __init__(self,nickname,
                 script,
                 aggressivity=58,
                 life_value=455,
                 ):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
        self.script = script

    def attack(self,enemy):
        print(self.script)
        enemy.life_value -= self.aggressivity


g1=Garen("德玛西亚之力","人在塔在")
g1.camp="诺克萨斯"
r1=Riven("瑞雯","断剑重铸之日,骑士归来之时")
g1.attack(r1)
print(r1.life_value)

结果:

人在塔在
356

Garen类和Riven类都有nickname、aggressivity、life_value、script四个变量属性和attack()函数属性,这里可以抽象出一个Hero类,里面有里面包含这些属性。

class Hero:
    def __init__(self, nickname,
                 script,
                 aggressivity,
                 life_value,
                 ):
        self.nickname = nickname
        self.aggressivity = aggressivity
        self.life_value = life_value
        self.script = script

    def attack(self,enemy):
        print("Hero.attack")
        enemy.life_value -= self.aggressivity

class Riven(Hero):
    camp='Noxus'
    def __init__(self,nickname,script,aggressivity=54,life_value=414):
        Hero.__init__(self,nickname,script,aggressivity,life_value)

    def attack(self,enemy):
        print(self.script)
        Hero.attack(self,enemy)

class Garen(Hero):
    camp='Demacia'
    def __init__(self,nickname,script,aggressivity=58,life_value=455):
        Hero.__init__(self, nickname, script, aggressivity, life_value)

    def attack(self,enemy):
        print(self.script)
        Hero.attack(self, enemy)

g1=Garen("德玛西亚之力","人在塔在")
g1.camp="诺克萨斯"
r1=Riven("瑞雯","断剑重铸之日,骑士归来之时")
g1.attack(r1)
print(r1.life_value)

结果:

人在塔在
Hero.attack
356

严格来说,上述Hero.init(self,...),不能算作子类调用父类的方法。因为我们如果去掉(Hero)这个继承关系,代码仍能得到预期的结果。
总结python中继承的特点:
1. 在子类中,并不会自动调用基类的__init__(),需要在派生类中手动调用。
2. 在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。
3. 先在本类中查找调用的方法,找不到才去基类中找。

1.3 继承的好处

提高代码的复用。

1.4 继承的弊端

     可能特殊的本类又有其他特殊的地方,又会定义一个类,其下也可能再定义类,这样就会造成继承的那条线越来越长,使用继承的话,任何一点小的变化也需要重新定义一个类,很容易引起类的爆炸式增长,产生一大堆有着细微不同的子类. 所以有个“多用组合少用继承”的原则。


# 2 什么是派生      派生就是子类在继承父类的基础上衍生出新的属性。子类中独有的,父类中没有的;或子类定义与父类重名的东西。子类也叫派生类。

3 组合

代码复用的重要的方式除了继承,还有组合。
组合,在一个类中以另一个类的对象作为数据属性,称为类的组合。

class Skill:
    def fire(self):
        print("release Fire skill")

class Riven:
    camp='Noxus'
    def __init__(self,nickname):
        self.nickname=nickname
        self.skill5=Skill().fire()#Skill类产生一个对象,并调用fire()方法,赋值给实例的skill5属性

r1=Riven("瑞雯")

结果:

release Fire skill

# 4 组合和继承的使用场景
  • 继承的方式
    通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
class Animal:
    def walk(self):
        print("Animal is walking")
    def eat(self):
        print("Animal is eating")

class Person(Animal):
    pass

p1=Person()
p1.walk()   #Animal is walking
  • 组合的方式
    用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如老师有生日,老师教python课程。
class Teacher:
    def __init__(self,name,sex,course):
        self.name=name
        self.sex=sex
        self.course = course

class Course:
    def __init__(self, name, period):
        self.name = name
        self.period = period

co=Course("python","7 m")
t1=Teacher("zhang","male",co)
print(t1.course.name)

结果:

python

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

5 接口和归一化设计

什么是接口?接口就是对外只提供抽象的方法的集合。

继承的两种用途:

  1. 继承基类的方法,并且做出自己的改变或者扩展(代码复用)
  2. 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。
class Interface:#定义接口Interface类来模仿接口的概念,python中没有interface关键字来定义一个接口。
    def read(self): #定接口函数read
        pass
        #raise AttributeError("read function must be implemented")    #如果不重写read函数就报错
        
    def write(self): #定义接口函数write
        pass
        #raise AttributeError("wirte function must be implemented")    #如果不重写write函数就报错
        
class Txt(Interface): #文本,具体实现read和write
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(Interface): #磁盘,具体实现read和write
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

     实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
     继承的第二种含义非常重要。它又叫“接口继承”。
     接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
     归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

6 抽象类

6.1 什么是抽象类

     与java一样,python也有抽象类的概念,但是需要借助模块实现。抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
     抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。

class Interface(metaclass=abc.ABCMeta):  # 定义接口Interface类来模仿接口的概念,python中没有interface关键字来定义一个接口。
    @abc.abstractclassmethod
    def read(self):  # 定接口函数read
        pass

    @abc.abstractclassmethod
    def write(self):  # 定义接口函数write
        pass

class Txt(Interface):  # 文本,具体实现read和write
    def read(self):
        print('文本数据的读取方法')

    def write(self):
        print('文本数据的读取方法')

class Sata(Interface):  # 磁盘,具体实现read和write
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

6.2 抽象类与接口

     抽象类的本质还是类,抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
     抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计 。