zoukankan      html  css  js  c++  java
  • python的类的继承和多继承

    一、类的继承

    •  面向对象三要素之一,继承Inheritance
    •  class Cat(Animal)这种形式就是从父类继承,继承可以让子类从父类获取特征(属性和方法)
    •  在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码,多复用,子类可以定义自己的属性和方法
    class Animal:
        def __init__(self,name):
            self._name = name
        
        def shout(self):
            print('{} shout'.format(self.__class__.__name__))
        
        @property  #属性装饰器
        def name(self):
            return self._name
    
    a = Animal('monster')
    a.shout()
    
    class Cat(Animal):
        pass
    
    cat = Cat('garfield')
    cat.shout()
    print(cat.name)
    
    父类: Animal就是Cat的父类,也称为基类,超类
    子类:Cat就是Animal的子类,也称为派生类
    如果类定义时,没有基类列表,等同于继承自object,在python3中,object类是所有对象的根基类


    1、查看继承的特殊属性和方法

    • __base__ : 类的基类
    • __based__ : 类的基类元组
    • __mro__ : 显示方法查找顺序,基类的元组
    • mro()方法 : 显示方法查找顺序,基类的元组
    • __subclasses__() : 类的子类列表

    2、继承中的访问控制

    •  从父类继承,自己没有的就可以到父类中找
    •  私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类的__dict_-中
    •  继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏了,子类和实例不可直接访问
    •  当私有变量所在的类内的方法中可以访问这个私有变量
    •  属性查找属性:实例的dict--》类的dict--》父类dict
    举例:
    class Animal:
        __COUNT = 0
        HEIGHT = 0
        
        def __init__(self,age,weight,height):
            self.__COUNT += 1
            self.age = age
            self.__weight = weight
            self.HEIGHT = height
        
        def eat(self):
            print('{} eat'.format(self.__class__.__name__))
        
        def __getweight(self):
            print(self.__weight)
         
        @classmethod
        def showcount1(cls):
            print(cls.__COUNT)
        
        @classmethod
        def __showcount2(cls):
            print(cls.__COUNT)
    
    class Cat(Animal):
        NAME = 'CAT'
        
    
    c = Cat(3,5,15)
    c.eat()
    print(c.HEIGHT)  
    #print(c.__COUNT)  #私有的不可访问
    #c.__showweight()  #私有的不可访问
    c.showcount1()   
    #c.__showcount2()  #私有的不可访问    
    print(c.NAME)
    print("{}".format(Animal.__dict__))
    print("{}".format(Cat.__dict__))
    print(c.__dict__)
    print(c.__class__.mro())

    3、方法的重写、覆盖overrride

    class Animal:
        def shout(self):
            print('Animal shout')
    
    class Cat(Animal):
        #覆盖了父类方法
        def shout(self):
            print('miao')
            
        #覆盖子类自身的方法,显示调用了父类的方法
        def shout(self):
            print(super())
            print(super(Cat,self))
            super().shout()
            super(Cat,self).shout()
            
            
    c = Cat()
    c.shout()


    4、继承中的初始化

        class A:
            def __init__(self,a):
                self.a = a
                
        class B(A):
            def __init__(self,b,c):
                self.b = b
                self.c = c
            
            def printv(self):
                print(self.b)
                print(self.a)  # 报错
    
        f = B(200,300)
        print(f.__dict__)
        print(f.__class__.__bases__)
        f.printv()
    
    上例代码可知:如果类B定义时声明继承自类A,则在类B中__bases__中是可以看到类A,但是这和是否调用类A的构造方法是两回事
    1、如果子类B没有定义init方法,初始化的时候会自动调用基类A是init方法
    
    class A:
        def __init__(self):
            self.a1 = 'a1'
            self.__a2 = 'a2'
            print('A init')
    
    class B(A):
        pass
    
    b = B()
    print(b.__dict__)
    
    2、如果子类B定义了init方法,实例的初始化不会调用父类的初始化init方法
    class A:
        def __init__(self):
            self.a1 = 'a1'
            self.__a2 = 'a2'
            print('A init')
    
    class B(A):
        def __init__(self):
            self.b1 = 'b1'
            print('B init')
    
    b = B()
    print(b.__dict__)
    
    3、通过上面分析,子类不会调用父类init方法,这导致没有实现继承效果,所以在子类的__init__方法中,
    应该显式调用父类的__init__方法
    
    class Animal:
        def __init__(self,age):
            print('Animal init')
            self.age = age
        
        def show(self):
            print(self.age)
        
    class Cat(Animal):
        def __init__(self,age,weight):
            #调用父类的__init__方法顺序决定着show方法的结果
            super().__init__(age)
            print('Cat init')
            self.age = age + 1
            self.weight = weight
    
    c = Cat(10,5)
    c.show()
    
    
    注意、调用父类的__init__方法,出现在不同的位置,


    二、多继承

    • OCP原则:多继承,少修改;继承的用途:增强基类,实现多

    1、多态

    •  在面向对象中,父类,子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态
    •  一个类继承自多个类就是多继承,它将具有多个类的特征

    2、多继承弊端

    •  多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性带来冲突
    •  如同一个孩纸继承了来自父母双方的特征
    •  多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言也舍弃了类的多继承

    3、python多继承实现

    •  多继承带来路径选择问题,究竟继承哪个父类的特征
    •  python使用MRO(method resolution order)解决基类搜索顺序问题

    4、多继承的缺点

    •  当类很多,继承复杂的情况下,继承路径太多,很难说清楚什么样的继承路径
    •  python语法是允许多继承,但是python代码是解释执行,只有执行到的时候才发现错误
    举例:
        class Printable:
            def _print(self):
                print(111,self.content)
    
        class Document: #第三方库,不允许修改
            def __init__(self,content):
                self.content = content
    
        class Word(Document): pass  #第三方库,不允许修改
        class Pdf(Document): pass   #第三方库,不允许修改
    
        class PrintableWord(Printable,Word): pass
        print(222,PrintableWord.__dict__)
        print(333,PrintableWord.mro())
    
        pw = PrintableWord('test string')
        pw._print()

    5、用装饰器增强

    •  用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
    •  优点:简单方便,在需要的地方动态增加,直接使用装饰器
    def printable(cls):
        def _print(self):
            print(self.content,'装饰器')
        
        cls.print = _print
        return cls
    
    class Document: #第三方库,不允许修改
        def __init__(self,content):
            self.content = content
    
    class Word(Document): pass  #第三方库,不允许修改
    class Pdf(Document): pass   #第三方库,不允许修改
    
    @printable #先继承,后装饰
    class PrintableWord(Word): pass
    print(PrintableWord.__dict__)
    print(PrintableWord.mro())
    pw = PrintableWord('test string')
    pw.print()


    6、Mixin的引用

    •  Mixin本质上就是多继承实现的,是一种组合的设计模式
    •  在面向对象的设计中,一个复杂的类往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起
    •  从设计的模式的角度来说,多组合,少继承
    举例:    
        class PrintableMixin:
            def print(self):
                print(self.content,'Mixin')
                
        def printable(cls):
            def _print(self):
                print(self.content,'装饰器')
            
            cls.print = _print
            return cls
    
        class Document: #第三方库,不允许修改
            def __init__(self,content):
                self.content = content
    
        class Word(Document): pass  #第三方库,不允许修改
        class Pdf(Document): pass   #第三方库,不允许修改
            
        class PrintableWord(PrintableMixin,Word): pass
        print(PrintableWord.__dict__)
        print(PrintableWord.mro())
    
        pw = PrintableWord('test string')
        pw.print()    
    
    Mixin就是其他类混合进来,同时带来了类的属性和方法,这里看来Mixin类和装饰器效果一样,也没有什么特别
    但是Mixin是类,就可以继承
        


    7、Mixin类的使用原则

    •   Mixin类中不应该显式的出现__init__初始化方法
    •   Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
    •   Mixin类的祖先类也应是Mixin类,使用时,Mixin类通常在继承列表的第一个位置
  • 相关阅读:
    layui 参照赋值的两种方式
    c笔记
    Linux操作系统笔记
    make笔记
    Gcc如何知道文件类型。
    #include <xxx.h>和#include "xxx.h"的区别
    GCC编译流程
    c++ Socket客户端和服务端示例版本三(多线程版本)
    c++ Socket客户端和服务端示例版本二
    c++ Socket客户端和服务端示例版本一
  • 原文地址:https://www.cnblogs.com/jiangzuofenghua/p/11413777.html
Copyright © 2011-2022 走看看