python开发基础----类(组合与继承)

组合:

定义一个人的类,人有头,躯干,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合

用途:

1、做关联

2、小的组成大的

class School:
    def __init__(self,name,addr):
        self.name = name
        self.addr = addr

    def get_name(self):
        return self.name

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

class terch:
    def __init__(self,name,age,school,course):
        self.name = name
        self.age = age
        self.school = school
        self.course = course


s1 = School("清华","北京")
c1 = Course("python",10,"1h",s1)
t1 = terch('alex',30,s1,c1)
print(c1.__dict__)
print(c1.school.get_name())
print(t1.__dict__)
学校、老师、课程的组合

 

继承:

1、什么是类的继承?

类的继承跟现实生活中的父、子、孙子、重孙子的继承关系一样,父类又称为基类。

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

子类会“”遗传”父类的属性,从而解决代码重用问题

查看继承

>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

 

 

在python中类的继承分为:单继承和多继承

class ParentClass1:
    pass

class ParentClass2:
    pass

class SubClass(ParentClass1): #单继承
    pass

class SubClass1(ParentClass1,ParentClass2): #多继承
    pass

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承

抽象即抽取类似或者说比较像的部分。

抽象分成两个层次: 

1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

2.将人,猪,狗这三个类比较像的部分抽取成父类。

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

 

 

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

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

 

 

 

2、子继承到底继承了父类的什么?

子类继承了父类的所有属性,如果子类有自定义属性跟父类重名了,会使用子类的属性。原理跟函数作用域差不多!当执行子类的某个属性时,会先从自己本身找,如果没有定义,就会取父类的属性字典里找,如果都没有找到,就会报错

 

3、什么时候用继承、什么时候用组合?

  •   当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。(例如,描述一个机器人类,机器人这个大类是由很多不相关的小类组成,如机械胳膊、腿类、身体类、电池类等)
  •         当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
    •   猫可以:喵喵叫、吃、喝、拉、撒
    •        狗可以: 汪汪叫、吃、喝、拉、撒
class Base:
    def chi(self):
        print("chi")
    def he(self):
        print("he")
    def la(self):
        print("la")
    def sa(self):
        print("sa")

class Mao(Base):
    def mmjiao(self):
        print("miaomiaojiao")

class Gou(Base):
    def wwjiao(self):
        print("wangwangjiao")

g1 = Gou()
m1 = Mao()
g1.chi()
g1.wwjiao()
m1.chi()
m1.mmjiao()
View Code

 

4、继承同时具有两种含义

含义一:继承基类的方法,并且做出自己的改变或扩展(代码重用,继承与派生,派生的意思就是子类做出了自己的改变或扩展)

含义二:声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并硬性规定实现接口定义的方法

实践中,继承的第一种含义意义并不大,甚至常常是有害的,因为它使得子类与基类出现强耦合。

继承的第二种含义非常重要。它又叫“接口继承”

接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”------这在程序设计上,叫做归一化。

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合--就像linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络、还是其他(当然,对于底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设置)

接口继承就是(基类)父类定义好2个函数属性(接口),所有的子类必须有这2个函数属性,缺一不可,不是说省代码的,是用来做强制性约束的

基类里面的方法不用具体的实现,只是一个规范而已

import abc
class Allfile(metaclass=abc.ABCMeta):  #强制约束
    @abc.abstractstaticmethod  #继承此类,必须重写这个方法,否则会直接报错
    def read(self):
        pass
    @abc.abstractstaticmethod
    def write(self):
        pass
class Disk(Allfile):
    def read(self):
        print("disk read")
    def write(self):
        print("disk write")
class Cdrom(Allfile):
    def read(self):
        print("cdrom read")
    def write(self):
        print("cdrom write")
class Mem(Allfile):
    def read(self):
        print("mem read")
    def write(self):
        print("mem write")
m1=Mem()
m1.read()
m1.write()

 

当类是经典类时,多继承情况下,会按照深度优先方式查找

 

 

当类是新式类时,多继承情况下,会按照广度优先方式查找

广度优先,已上图为例,是从左边开始找一直找到B,然后再从右边找,一直找到A

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

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

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

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

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

class A:
def test(self):
print("A")
class B(A):
def test(self):
print("B")
class C(A):
def test(self):
print("C")

class D(B):
def test(self):
print("D")
class E(C):
def test(self):
print("E")
class F(D,E):
def test(self):
print("F")

f = F()
print(F.__mro__)

结果:
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

只要基类没有继承object,那就是经典类,这个只有在python2中有区分,在python2中的经典类是没有__mro__属性的,查找顺序是F-->D-->B-->A-->E-->C

 

在子类中调用父类的方法

子类继承了父类的方法,然后想进行修改 ,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法

方法一:父类名.父类方法()

class Vehicle:
    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 show_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)

    def run(self):
        Vehicle.run(self)
        print("%s %s 线,开动了" %( self.name , self.line))

line3 = Subway("深圳地铁",'10km/s',1000000,'',3)
line3.show_info()
line3.run()

方法二:使用super()方法

class Vehicle:
    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)
        super().__init__(name,speed,load,power)
        self.line = line

    def show_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)

    def run(self):
        # Vehicle.run(self)
        super().run()
        print("%s %s 线,开动了" %( self.name , self.line))

line3 = Subway("深圳地铁",'10km/s',1000000,'',3)
line3.show_info()
line3.run()

如果不适用super(),修改代码很麻烦,当你使用super()函数时,python会在MRO列表上继续搜索下一个类,只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次

 

posted @ 2019-09-18 09:27  Mr-谢  阅读(398)  评论(0)    收藏  举报