、基本概念
面向对象三要素之一:继承inheritance
继承表达式:class Cat(Anaimal),继承可以让子类从父类获取特征(属性和方法)
父类:Anaimal 就是Cat的父类,也称为基类,超类
子类:Cat就是Anaimal的子类,也称为派生类
2、定义
格式如下:
class 子类名(父类名): 语句块
如果类定义时,没有基类列表,等同于继承自object,在Python3中,object类是所有对象的根基类
只在python3 中是可以等价的,python2中不同的
class A: pass 等同于 class A(object): pass
注意:python支持多继承,继承也可以多级。
查看继承的特殊属性和方法有:
特殊属性和方法 | 含义 | 示例 |
__base__ | 类的基类 | |
__bases__ | 类的基类元组 | |
__mor__ | 显示方法查找顺序,基类的元组 | |
mro()方法 | 同上,返回列表 | |
__subclassedd__() | 类的子类列表 | print(Anaimal.__subclasses__()) |
3、继承中的访问控制:
举例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Anaimal: 2 __COUNT = 100 3 HEIGHT = 0 4 5 def __init__(self, age, weight, height): 6 self.__COUNT += 1 7 self.age = age 8 self.__weight = weight 9 self.HEIGHT = height 10 11 def eat(self): 12 print('{} eat'.format(self.__class__.__name__)) 13 14 def __getweight(self): 15 print(self.__weight) 16 17 @classmethod 18 def showcount1(cls): 19 print(cls) 20 print(cls.__dict__) 21 print(cls.__COUNT) 22 23 @classmethod 24 def __showcount2(cls): 25 print(cls.__COUNT) 26 27 def showcount3(self): 28 print(self.__dict__) 29 print(self.__COUNT)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from t1 import Anaimal 2 3 class Cat(Anaimal): 4 NAME = 'CAT' 5 __COUNT = 200 6 7 # c = Cat() 缺参数,报错 8 c = Cat(3, 5, 15)# 使用父类初始化函数进行初始化 9 # print(c.HEIGHT) # 自己没有,调用父类的属性 10 # c.eat()# 调用父类的方法 11 # print(c._Anaimal__weight)# 获取父类初始化函数中的属性 12 # c._Anaimal__getweight() 13 14 c.showcount3()
总结:
- 从父类继承,自己没有的,就可到父类中找
- 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或示例的__dict__中,知道这个名称就可以直接找到这个隐藏的变量,这个是黑魔法,慎用
- 继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法可以访问这个私有变量
- Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制
- 私有的只个自己用,并不是给其他的类或实例用的,出去的,也就是可以直接在类外调用的,都是共有的
- 事实上,在类内部是可以随便访问的,直接 .__xx 调用的,但事实上,在外部,看到的不是这个名字
- 如果想获取私有属性,可以给一个调用方法 如:getage 返回 self.__age,或者使用属性装饰器property,从而不需要知道隐藏属性的真是名称,直接访问
- 私有方法,就不要调来调去了,虽然是可以的!
属性查找顺序:
实例的__dict__ ----> 类的 __dict__ ---> 如果有继承 -----> 父类的__dict__
如果一直没找到,抛异常,找到了,立即返回
4、方法的重写、覆盖override
举例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Anaimal: 2 def shout(self): 3 print('Animal') 4 5 class Cat(Anaimal): 6 def shout(self): 7 print(super()) 8 print(super(Cat, self)) 9 super().shout() 10 super(Cat, self).shout() 11 self.__class__.__base__.shout(self) 12 print('miao') 13 14 # a = Anaimal() 15 # a.shout() 16 c = Cat() 17 c.shout() 18 # print(a.__dict__) 19 # print(c.__dict__) 20 # print(Anaimal.__dict__) 21 # print(Cat.__dict__)
对于类方法和静态方法也是一样的:
1 class Animal: 2 @classmethod 3 def class_method(cls): 4 print('class_Animal') 5 6 @staticmethod 7 def static_method(): 8 print("static_Animal") 9 10 class Cat(Animal):pass 11 # @classmethod 12 # def class_method(cls): 13 # print('class_Cat') 14 # 15 # @staticmethod 16 # def static_method(): 17 # print("static_Cat") 18 19 c = Cat() 20 c.class_method() 21 c.static_method()
5、继承中的初始化
举例:
从上面的代码可以看出:
如果类B 定义时声明继承 类A,则在类B 中__bases__中是可以看到类A的,但是这和是否调用类A 的构造方法时两回事
如果B中调用了A 的构造方法,就可以拥有父类的属性了,
举例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class A: 2 def __init__(self, a, d=10): 3 self.__d = d 4 self.a = a 5 6 class B(A): 7 def __init__(self, b, c): 8 super().__init__(b+c, b-c) 9 # 等价 A.__init__(self, b+c, b-c) 10 self.b = b 11 self.c = c 12 13 def printv(self): 14 print(self.b) 15 print(self.a) 16 print(self._A__d) 17 18 f = B(200, 300) 19 print(f.__dict__) 20 print(f.__class__.__bases__) 21 f.printv()
总结:
作为好习惯,如果分类定义了__init__方法,要在子类的__init__中调用它。显式的调用
除非:子类没有定义__init__,会隐式去调用或者继承父类的__init__,子类一旦定义了__init__,就不会自动调用父类的init
如何正确的初始化:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Animal: 2 def __init__(self, age): 3 print('Animal') 4 self.age = age 5 6 def show(self): 7 print(self.age) 8 9 class Cat(Animal): 10 def __init__(self, age, weight):# 没有调用父类的init,这就导致没有实现继承效果 11 print('Cat') 12 self.age = age + 1 13 self.weight = weight 14 15 c = Cat(10, 5) 16 print(c.__dict__) 17 c.show() 18 19 print('-' * 30) 20 class Animal: 21 def __init__(self, age): 22 print('Animal') 23 self.age = age 24 25 def show(self): 26 print(self.age) 27 28 29 class Cat(Animal): 30 def __init__(self, age, weight): 31 print('Cat') 32 super().__init__(age) 33 self.age = age + 1 # 覆盖之前的self.age 34 self.weight = weight 35 36 37 c = Cat(10, 5) 38 print(c.__dict__) 39 c.show() 40 41 42 print('-' * 30) 43 class Animal: 44 def __init__(self, age): 45 print('Animal') 46 self.age = age 47 48 def show(self): 49 print(self.age) 50 51 52 class Cat(Animal): 53 def __init__(self, age, weight): 54 print('Cat') 55 self.age = age + 1 56 self.weight = weight 57 super().__init__(age) 58 59 60 c = Cat(10, 5) 61 print(c.__dict__) 62 c.show()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Cat 2 {'age': 11, 'weight': 5} 3 11 4 ------------------------------ 5 Cat 6 Animal 7 {'age': 11, 'weight': 5} 8 11 9 ------------------------------ 10 Cat 11 Animal 12 {'age': 10, 'weight': 5} 13 10
例子中打印10 的,原因看__dict__ 就知道了,因为父类Animal的show方法中_age会被释放为_Animal__age,因此。显式10,而不是11,这样的设计不会,子类应该显式自己的属性值最好
解决办法:
一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类。