• Python面向对象编程(二)


    1.继承与派生

    上文我们已经说过,Python中一切皆对象。我们从对象中抽取了共同特征和技能,得到了类的概念。类与类之间也有共同特征,我们可以从有共同特征和技能的类中提取共同的技能和特征,叫做父类。

    比如老师和学生,都有名字,年纪,生日,性别等等,都会走,说话,吃饭。。。我们就可以从老师和学生中总结出来一个‘人’类,称为父类,那老师和学生就是‘人’类的子类,子类继承父类,就有了父类的特征和方法。

    继承是一种什么‘是’什么的关系,继承是一种产生新类的方法,当然目的也是为了减少代码重用。

    继承的 基本形式是:

    class People:
        pass
    class Student(People):#People称为基类或者父类
        pass

    在Python中支持多继承,一个子类可以继承多个父类

    我们可以通过__bases__的方法查看继承的所有父类,会返回一个元组。 

    class People:
        pass
    class Animals:
        pass
    class Student(People,Animals):
        pass
    
    print(Student.__bases__)#(<class '__main__.People'>, <class '__main__.Animals'>)
    print(People.__bases__)#(<class 'object'>,)

    可以看到,在People父类中,默认也继承了一个object类,这就是新式类和经典类的区别:
    凡是继承了object类的类及其子类,都称为新式类,没有继承object类的类,称为经典类。

    在Python 3中,默认就是新式类,而在Python2.X中,默认都是是经典类

    继承怎么减少代码呢?看例子

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def walk(self):
            print('%s is walkig'%self.name)
    
    class Teacher(People):
        def __init__(self,name,age,level):
            People.__init__(self,name,age)
            self.level=level
    
    t1=Teacher('zhang',18,10)
    print(t1.level) #10
    print(t1.name)  #zhang          子类可以用父类定义的属性
    t1.walk()   #zhang is walking   子类无需定义就可以用父类的方法
    print(issubclass(Teacher,People))   #True查看Teacher类是不是People类的子类

    从上面的例子中可以看到,Teacher类继承了父类People类,但是Teacher又有自己特有的属性level,子类也可以定义自己独有的方法,甚至可以和父类的方法重名,但是执行时会以子类定义的为准。

    这就叫做派生

    2.组合

    继承是解决什么‘是’什么的问题,那还有一种场景就是什么有什么,比如老师有生日,学生也有生日,生日有年月日这些属性,如果每个类都写的话,又是重复代码。但是又不能让学生和老师继承生日类。这时就用到了组合。组合就是解决什么‘有’什么的问题。看例子

    class Date:
        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day
        def tell_birth(self):
            print('出生于%s年%s月%s日'%(self.year,self.mon,self.day))
    
    class Teacher:
        def __init__(self,name,age,year,mon,day):
            self.name=name
            self.age=age
            self.birth=Date(year,mon,day)
    t=Teacher('egon',19,2010,10,10)
    print(t.birth)          #<__main__.Date object at 0x0000017E559380F0>
    t.birth.tell_birth()    #出生于2010年10月10日

    什么?嫌参数太多?*args学过吧,你高兴就好

     1 class Date:
     2     def __init__(self,year,mon,day):
     3         self.year=year
     4         self.mon=mon
     5         self.day=day
     6     def tell_birth(self):
     7         print('出生于%s年%s月%s日'%(self.year,self.mon,self.day))
     8 
     9 class Teacher:
    10     def __init__(self,name,age,*args):
    11         self.name=name
    12         self.age=age
    13         self.birth=Date(*args)
    14 t=Teacher('egon',19,2010,10,10)
    15 print(t.birth)          #<__main__.Date object at 0x0000017E559380F0>
    16 t.birth.tell_birth()    #出生于2010年10月10日
    View Code

    3.抽象类与接口

    继承有两种用途:1.代码重用,子类继承父类的方法

            2.声明某个子类兼容于某父类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

    需要注意的是,Python中并没有接口的关键字,我们只能是模仿接口的功能
    比如在 Python中,一切皆文件嘛,那程序是文件,硬件是文件,文本文档也是文件,我们知道什么叫文件呢,就是能读能写,那程序,文本文档这些,都应该有读和写的功能,我们来模拟一下

    class Interface:
        def read(self):
            pass
        def write(self):
            pass
        
        
    class Txt(Interface):
        def read(self):
            print('文本文档的读取方式')
        def write(self):
            print('文本文档的写入方式')
            
    class Sata(Interface):
        def read(self):
            print('硬盘文件的读取方式')
        def write(self):
            print('硬盘文件的写入方式')
    
    class process(Interface):
        def read(self):
            print('进程数据的读取方式')
        def write(self):
            print('进程数据的写入方式')
    View Code

    这么做的意义就是:我们不需要知道子类有什么具体的方法,既然他们继承了文件类,那他们就是文件,那他们就有读和写这两个功能

    父类限制了子类子类必须有read和write这两个方法,而且名字也必须一样(当然现在只是我们主观上的限制,一会我们说完抽象类,就可以从代码级别上限制了),这样就实现了统一,模拟了接口的概念,这就是归一化设计。在归一化设计中,只要是基于一个接口设计的类,那么所有的这些类实例化出来的对象,在用法上是一样的

    我们再来说一下抽象类:

    Python中的抽象类需要导入一个模块来实现。抽象类只能被继承,不能被实现

    抽象类的写法:

    import abc
    class File(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def read(self):
            pass
        @abc.abstractmethod
        def write(self):
            pass
    #父类使用了抽象类,那子类就必须继承父类的方法,而且名字也必须一样
    #这样就实现了代码级别的限制
    
    class Txt(File):
        def read(self):
            print('文本文档的读取方式')
        def write(self):
            print('文本文档的写入方式')

    4.继承的实现原理

    1)继承顺序:

    python支持多继承,当一个类继承多个父类时,继承顺序是怎样的呢?这个顺序在新式类和经典类中是不一样的。

    在新式类中,继承顺序是广度优先,在经典类中是深度优先,举个栗子:

    图不重要,看内容
    在这个图中,H是子类,H继承E,F,G,E,F,G,又分别继承B,C,D,B,C,D,同时继承A

    在新式类中的顺序是:H E B F C G D A 

    在经典类中的顺序是:H E B A F C G D

    2)继承原理:

    当我们定义一个类后,Python就会根据上面的继承规律解析出一个继承顺序的列表(MRO列表),可以通过mro()查看,但是这个方法只有在新式类中才有,经典类没有

    mro

     3)super()方法

    我们之前用继承是怎么用的来着,

    class Parent(object):
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
    class Child(Parent):
        def __init__(self,name,age,salary):
            Parent.__init__(self,name,age,salary)
            self.salary=salary

    这其实是和继承没啥关系的写法,如果父类名字改了,在子类中也要改,更优雅的写法是用super()

    class Parent(object):
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
    class Child(Parent):
        def __init__(self,name,age,salary):
            super().__init__(name,age)
            self.salary=salary

    这是python3中的写法,如果是python2,super后面的括号里要写(Child,self)

    注意:super()方法只适用于新式类

    如果是多继承的关系,就用到mro列表,如果就是要继承多个父类的方法,那就还是乖乖的用以前指名道姓的方法引用

  • 相关阅读:
    第二冲刺阶段个人博客
    单元测试
    软件工程个人总结
    梦断代码阅读笔记03
    梦断编码阅读笔记02
    梦断代码阅读笔记01
    padding margin
    MVC模式在Java Web应用程序中的实例分析
    小问题
    MVC理解
  • 原文地址:https://www.cnblogs.com/zhang-can/p/7142266.html
Copyright © 2020-2023  润新知