类的继承
定义:
继承格式如下
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