day25---面向对象继承与多态

1. 面向对象的继承

  • 定义:继承是一种创建新类的方式

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

  • 方式:

    • 单继承:所有的类默认都会继承'object'
    • 多继承:继承多个类时,使用逗号分割,默认继承最左边的父类
  • __base__:只查看从左到右继承的第一个子类;如果没有继承,默认返回:<class 'object'>

class Biology:
        pass
class Animal:
        pass
class Cat(Biology):
        pass
class Dog(Animal):
        pass
class Monkey(Biology, Animal):
        pass
print(Biology.__base__)
print(Animal.__base__)
print(Cat.__base__)
print(Dog.__base__)
print(Monkey.__base__)
>>>
<class 'object'>
<class 'object'>
<class '__main__.Biology'>
<class '__main__.Animal'>
<class '__main__.Biology'>
  • __bases__:查看所有继承的父类,返回元组格式;如果没有继承,默认返回:(<class 'object'>,)

class Biology:
        pass
class Animal:
        pass
class Cat(Biology):
        pass
class Dog(Animal):
        pass
class Monkey(Biology, Animal):
     pass
print(Biology.__bases__)
print(Animal.__bases__)
print(Cat.__bases__)
print(Dog.__bases__)
print(Monkey.__bases__)
>>>
(<class 'object'>,)
(<class 'object'>,)
(<class '__main__.Biology'>,)
(<class '__main__.Animal'>,)
(<class '__main__.Biology'>, <class '__main__.Animal'>)

2. 类的说明

  • 查看类的属性:

    • dir(类名):查出的是一个名字列表,里面包含这个类的所有方法
    • 类名.__dict__:查出的是一个字典,key为属性名,value为属性值
  • 特殊的类属性:

    • 类名.__name__:类的名字,字符串类型
    • 类名.__doc__:类的文档,字符串类型
    • 类名.__base__:类的第一个父类
    • 类名.__bases__:以元组的形式返回类的所有父类
    • 类名.__dict__: 以字典的形式返回类的所有属性
    • 类名.__module__:类所在的模块名,字符串类型
    • 实例名.__class__:返回实例名所属的类名

3. 继承与抽象(先抽象后继承)

+ ##### 抽象(从具体到模糊的过程):即抽取类似或者说比较像的部分;抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
+ ##### 继承(从模糊到具体的过程):是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构

4. 继承的派生

  • 派生:子类在父类方法和属性的基础上产生了新的方法和属性

  • 派生属性:在子类的__init__中使用父类的__init__的属性。调用父类的属性时,可以使用父类名.__init__;也可以使用super()方法,super()方法中的参数在类中使用可以省略

  • 派生方法:在子类中增加父类中没有的方法

  • 子类中有的方法,就使用子类的

    • 父类中没有的方法,子类中有,会使用子类的
    • 父类中有的方法,子类也有,会使用子类的
    • 父类中有的方法,子类中没有,会使用父类的
  • 父类中有的方法,子类也有,如果想坚持使用父类中的或父类子类都想用,需要使用:父类名.父类的方法名(子类的对象)

    • 经典类(python2默认的面向对象类型):类名.方法名(子类对象名),类内类外调用方式一样
    • 新式类(python3默认的面向对象类型):super(子类, 子类对象名).方法名(),如果在类中调用可以省略super()方法中的参数
    class Life:
            def __init__(self, name, age):
                self.name = name
                self.age = age
            def eat(self):
                print('吃...')
            def drink(self):
                print('喝...')
    class Person(Life):
            def __init__(self, name, age, nation):
                Life.__init__(self, name, age)
                self.nation = nation
            def get_money(self):
                print('挣钱。。。')
            def eat(self):
                print('人吃饭。。。')
    class Dog(Life):
            def __init__(self, name, age, breed):
                super(Dog, self).__init__(name, age)
                self.breed = breed
            def watch_door(self):
                print('看门。。。')
            def drink(self):
                print('狗喝水。。。')
    yan = Person('岩哥', 25, '汉族')
    king = Dog('斑马', 2, '金毛')
    print(yan.name)
    print(king.age)
    print('-' * 20)
    print(yan.nation)
    print(king.breed)
    print('-' * 20)
    yan.get_money()
    king.watch_door()
    print('-' * 20)
    yan.eat()
    super(Person, yan).eat()
    king.eat()
    print('-' * 20)
    yan.drink()
    king.drink()
    super(Dog, king).drink()
    >>>
    岩哥
    2
    --------------------
    汉族
    金毛
    --------------------
    挣钱。。。
    看门。。。
    --------------------
    人吃饭。。。
    吃...
    吃...
    --------------------
    喝...
    狗喝水。。。
    喝...
    

5. 钻石继承

  • 继承顺序

    • pyhton可以继承多个类,而其他语言只能继承一个类
    • python的类如果继承了多个类,寻找的方式有两种:
      • 深度优先:当类是经典类时,多继承情况下会按照深度优先的方式查找
      • 广度优先:当类是新式类时,多继承情况下会按照广度优先的方式查找
    • 经典类和新式类从字面意思能看出是一个老的一个新的。新式类比经典类多了很多的功能,推荐使用新式类。在写法上,类或者父类都继承了Object类,就代表是新式类,反之是经典类。
    # O1和O2都是经典类
    class O1:
            pass
    class O2(O1):
            pass
    
    # N1和N2都是新式类
    class N1(object):
            pass
    class N2(N1):
            pass
    
  • 继承原理

    • mro():以列表的形式返回当前类所有父类的查找顺序。mro()方法只有新式类才有,经典类没有
    • 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
    • 父类的MRO列表并遵循如下三条准则:
      • 子类会先于父类被检查
      • 多个父类会根据它们在列表中的顺序被检查
      • 如果对下一个类存在两个合法的选择,优先选择第一个父类
  • 钻石继承的验证(在python2中操作)

# 经典类(三层)的继承顺序
class D:
        pass
class C(D):
        pass
class B(D):
        pass
class A(B, C):
        pass
print(A.mro())
>>>
        print(A.mro())
AttributeError: class A has no attribute 'mro'
# python2不支持mro()方法,正确的顺序是:A-->B-->D-->C
# 经典类(四层)的继承顺序
class F:
        pass
class E(F):
        pass
class D(F):
        pass
class C(E):
        pass
class B(D):
        pass
class A(B, C):
        pass
print(A.mro())
>>>

        print(A.mro())
AttributeError: class A has no attribute 'mro'
# python2不支持mro()方法,正确的顺序是:A-->B-->D-->F-->C-->E
# 新式类(三层)的继承顺序
class D(object):
        pass
class C(D):
        pass
class B(D):
        pass
class A(B, C):
        pass
print(A.mro())
>>>
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>]
# 新式类(四层)的继承顺序
class F(object):
        pass
class E(F):
        pass
class D(F):
        pass
class C(E):
        pass
class B(D):
        pass
class A(B, C):
        pass
print(A.mro())
>>>
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <type 'object'>]

6. 抽象类与接口类

  • 继承有两种用途:

    • 继承基类的方法,并且做出自己的改变或者扩展(代码重用)
    • 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
  • 抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

  • 如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性

  • 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性

  • 抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

  • 多继承问题:

    • 在继承抽象类的过程中,应该尽量避免多继承
    • 在继承接口的时候,应该多继承接口
  • 方法的实现:

    • 在抽象类中,可以对一些抽象方法做出基础实现
    • 在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
  • 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

  • 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口

7. 多态

  • 多态:多态指的是一类事物有多种形态;python自带多态功能

  • 多态性:多态性是指在不考虑实例类型的情况下使用实例

  • 鸭子类型:python崇尚鸭子类型

posted @ 2017-11-21 16:40  _岩哥  阅读(130)  评论(0)    收藏  举报