类的多继承和Mixin

类的继承

定义:
继承格式如下

class 子类名(基类1[,基类2,...]):
    语句块

如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基类

  • 查看继承的特殊属性和方法有 (与实例没有关系,指的是类与类的关系)
特殊属性和方法含义示例
__base __类的基类(父类)
__bases __类的基类元组(父类元组)
__mro __显示方法查找顺序,基类的元组
mro()方法同上,返回列表int.mro()
__subclasses __()类的子类列表int.__ subclasses__()
class Animal:      
    def shout(self): 
        print('{} shouts'.format(self.__class__.__name__))    

class Cat(Animal):    
    pass

class Dog(Animal):    
    pass
    
print(Cat.__base__)    #类的基类(父类)
print(Cat.__bases__)
print(Animal.__base__)
print(Animal.__bases__)
print(Animal.__subclasses__())   #类的子类列表,用方法查看
---------------------------------------------------------------
<class '__main__.Animal'>
(<class '__main__.Animal'>,)
<class 'object'>
(<class 'object'>,)
[<class '__main__.Cat'>, <class '__main__.Dog'>]

继承中的访问控制

class Animal:    
    __COUNT = 100    
    HEIGHT = 50    
    
    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):      # 私有方法,相当于 _Animal__getweight(self)
        print(self.__weight)    

    @classmethod    
    def showcount1(cls):
        print(cls)
        print(cls.__dict__)
        print(cls.__COUNT)    #相当于Cat_Animal__COUNT  私有属性,只能是当前类的

    @classmethod    
    def __showcount2(cls):
        print(cls.__COUNT) 

    def showcount3(self):
        print(self.__COUNT) # 此处是实例为参数

class Cat(Animal):
    NAME = 'CAT'    
    __COUNT = 200
    
c = Cat(3, 5, 20)  #实例化对象
--------------------------------------------------------------------------------
print(Cat._Cat__COUNT)      >>> 200     # 子类访问子类自己的私有属性    (属于类访问)
print(Cat.HEIGHT)           >>> 50      # 子类访问父类的属性           (属于类访问)
print(Cat._Animal__COUNT)   >>> 100     # 子类访问父类的私有属性       (属于类访问)
print(c._Animal__COUNT)     >>> 101     # 实例访问继承基类的私有属性   (属于实例访问)
print(c._Cat__COUNT)        >>> 200     # 实例访问父类的私有属性       (属于实例访问)
Animal._Animal__getweight(c)  #注意要传入参数 
c.showcount1()                # 注意输出结果
#c.__showcount2() # 不可以直接访问,__showcount2()为Animal的私有属性
c._Animal__showcount2()
c.showcount3()                # 注意输出结果
print(c.NAME)
print("{}".format(Cat.__dict__))
print(c.__dict__)
print(c.__class__.mro())
--------------------------------------------------------------------------------
5
<class '__main__.Cat'>
{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
100
100
101
CAT
{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
{'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 5, 'HEIGHT': 20}
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

从父类继承,自己没有的,就可以到父类中找。
私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的__dict__中。
知道这个新名称就可以直接找到这个隐藏的变量.

  • 总结:

    • 继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量(指在类中,包含这个私有变量的方法可以访问)
  • 实例属性查找顺序

    • 实例的__dict__ ===》类__dict__ =如果有继承=》父类 dict 如果搜索这些地方后没有找到就会抛异常,先找到就立即返回了

方法的重写、覆盖override

super()可以访问到父类的类属性
继承时使用初始化

作为好的习惯,如果父类定义了__init__方法,你就该在子类的__init__中调用它。
子类什么时候自动调用父类的__init__方法呢?
子类实例一旦定义了初始化__init__方法,就不会自动调用父类的初始化__init__方法,需要手动调用

class Animal:    
    def __init__(self, age):
        print('init in Animal')
        self.__age = age  
        
    def show(self):
        print(self.__age)
        
class Cat(Animal):    
    def __init__(self, age, weight):
        # 调用父类的__init__方法的顺序有时决定着show方法的结果
        super().__init__(age)
        print('init in Cat')
        self.__age = age + 1
        self.__weight = weight
        #super().__init__(age)
    def show(self):
        print(self.__age)
        
c = Cat(10, 5)
c.show() 
print(c.__dict__)
---------------------------------------------------------------
init in Animal
init in Cat
11
{'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}

多继承

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

  • 语法:
class ClassName(基类列表):  
        类体
  • Python使用MRO(method resolution order方法解析顺序)解决基类搜索顺序问题。
    历史原因,MRO有三个搜索算法:

    • 经典算法,按照定义从左到右,深度优先策略。2.2版本之前左图的MRO是MyClass,D,B,A,C,A
    • 新式类算法,是经典算法的升级,深度优先,重复的只保留最后一个。2.2版本左图的MRO是MyClass,D,B,C,A,object
    • C3算法,在类被创建出来的时候,就计算出一个MRO有序列表。2.3之后,Python3唯一支持的算法左图中的MRO是MyClass,D,B,C,A,object的列表 C3算法解决多继承的二义性
  • 经典算法有很大的问题,如果C中有覆盖A的方法,就不会访问到了,因为先访问A(深度优先)。新式类算法,依然采用了深度优先,解决了重复问题,但是同经典算法一样,没有解决继承的单调性。

多继承的缺点
  • 当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径。
  • Python语法是允许多继承,但Python代码是解释执行,只有执行到的时候,才发现错误。
  • 团队协作开发,如果引入多继承,那代码很有可能不可控。
  • 不管编程语言是否支持多继承,都应当避免多继承。
Mixin类 :
  • 抽象方法(抽象基类)
    • 基类中只定义,不实现的方法,称为“抽象方法”
    • 抽象方法,是缺失功能,功能不完善,但是要求子类必须实现此功能,所以就约束了子类必须覆盖此抽象方法
    • 只要基类中出现一个抽象方法,就称为抽象类
import math
class Square:
    def area(self):
        print('请计算完成',self.__class__.__name__,"的面积")

class Triangle(Square):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


class Circle(Square):
    def __init__(self, r):
        self.r = r

    def area(self):
        cirarea = math.pi*self.r
        return cirarea
        
tri =Triangle(3,4,5)
tri.area()                  # 此处调用了基类中的抽象方法,没有覆盖,所以没有计算出面积     
cir = Circle(5)
print(cir.area())           # 此处调用了area方法覆盖了基类中的抽象方法
-----------------------------------------------------
请计算完成 Trangle 的面积
15.707963267948966
增强类功能

普通方法_装饰器

class XiaoMiPart():pass

class XiaoMiPhone(XiaoMiPart):
    def __init__(self):
        pass                # phione 第三方库,类中不允许插入代码  
    
class XiaoMiTV(XiaoMiPart):
    def __init__(self):
        pass                # phione 第三方库,类中不允许插入代码
###################################################################

#当第三方库已经足够完善的时候,最好不要在第三方的库里更改代码增加功能,可以
#新建一个类继承要增强功能的类

#装饰器
def mark(cls):
    def fx(self,mark):
        self.mark = mark  # 增加xiaomi说明的标签
        print(self.mark)
    cls.mark = fx
    return cls

@mark
class PhoneMark(XiaoMiPhone):pass

@mark
class TVMark(XiaoMiTV):pass

xiaomi = PhoneMark()
xiaomi.mark('xiaomiPhone')

xiaomiTV = TVMark()
xiaomiTV.mark('xiaomiTV')   
----------------------------------------------
xiaomiPhone
xiaomiTV
  • 装饰器优点:简单方便,在需要的地方动态增加,直接使用装饰器。可以为类灵活的增加功能

Mixin方法

class XiaoMiPart():pass

class XiaoMiPhone(XiaoMiPart):
    def __init__(self):
        pass                # phion 第三方库,类中不允许插入代码  
    
class XiaoMiTV(XiaoMiPart):
    def __init__(self):
        pass                # phion 第三方库,类中不允许插入代码
###################################################################

#当第三方库已经足够完善的时候,最好不要在第三方的库里更改代码增加功能,可以
#新建一个类继承要增强功能的类

#装饰器
class MarkMixin:
    def mark(self,mar):
        self.mar = mar
        print(self.mar)

class PhoneMark(MarkMixin,XiaoMiPhone):pass

class TVMark(MarkMixin,XiaoMiTV):pass

xiaomi = PhoneMark()
xiaomi.mark('xiaomiphone')

xiaomiTV = TVMark()
xiaomiTV.mark('xiaomiTV')
----------------------------------------------
xiaomiPhone
xiaomiTV
  • Mixin就是其它类混合进来,同时带来了类的属性和方法。这里看来Mixin类和装饰器效果一样,也没有什么特别的。但是Mixin是类,就可以继承

Mixin类

Mixin本质上就是多继承实现的,Mixin体现的是一种组合的设计模式

  • Mixin类的使用原则
    • Mixin类中不应该显式的出现__init__初始化方法
    • Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
    • Mixin类的祖先类也应是Mixin类

使用时,Mixin类通常在继承列表的第一个位置,例如:class PhoneMark(MarkMixin,XiaoMiPhone):pass

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值