、基本概念
面向对象三要素之一:继承inheritance
继承表达式:class Cat(Anaimal),继承可以让子类从父类获取特征(属性和方法)
父类:Anaimal 就是Cat的父类,也称为基类,超类
子类:Cat就是Anaimal的子类,也称为派生类
2、定义
格式如下:
class子类名(父类名):
语句块
如果类定义时,没有基类列表,等同于继承自object,在Python3中,object类是所有对象的根基类
只在python3 中是可以等价的,python2中不同的
classA:pass等同于classA(object):pass
注意:python支持多继承,继承也可以多级。
查看继承的特殊属性和方法有:
特殊属性和方法
含义
示例
__base__
类的基类
__bases__
类的基类元组
__mor__
显示方法查找顺序,基类的元组
mro()方法
同上,返回列表
__subclassedd__()
类的子类列表
print(Anaimal.__subclasses__())
3、继承中的访问控制:
举例:
1 classAnaimal:2 __COUNT = 100
3 HEIGHT =04
5 def __init__(self, age, weight, height):6 self.__COUNT += 1
7 self.age =age8 self.__weight =weight9 self.HEIGHT =height10
11 defeat(self):12 print('{} eat'.format(self.__class__.__name__))13
14 def __getweight(self):15 print(self.__weight)16
17 @classmethod18 defshowcount1(cls):19 print(cls)20 print(cls.__dict__)21 print(cls.__COUNT)22
23 @classmethod24 def __showcount2(cls):25 print(cls.__COUNT)26
27 defshowcount3(self):28 print(self.__dict__)29 print(self.__COUNT)
t1.py
1 from t1 importAnaimal2
3 classCat(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()
t2.py
总结:
从父类继承,自己没有的,就可到父类中找
私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或示例的__dict__中,知道这个名称就可以直接找到这个隐藏的变量,这个是黑魔法,慎用
继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法可以访问这个私有变量
Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制
私有的只个自己用,并不是给其他的类或实例用的,出去的,也就是可以直接在类外调用的,都是共有的
事实上,在类内部是可以随便访问的,直接 .__xx 调用的,但事实上,在外部,看到的不是这个名字
如果想获取私有属性,可以给一个调用方法 如:getage 返回 self.__age,或者使用属性装饰器property,从而不需要知道隐藏属性的真是名称,直接访问
私有方法,就不要调来调去了,虽然是可以的!
属性查找顺序:
实例的__dict__ ----> 类的 __dict__ ---> 如果有继承 -----> 父类的__dict__
如果一直没找到,抛异常,找到了,立即返回
4、方法的重写、覆盖override
举例:
1 classAnaimal:2 defshout(self):3 print('Animal')4
5 classCat(Anaimal):6 defshout(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 classAnimal:2 @classmethod3 defclass_method(cls):4 print('class_Animal')5
6 @staticmethod7 defstatic_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 的构造方法,就可以拥有父类的属性了,
举例:
1 classA:2 def __init__(self, a, d=10):3 self.__d =d4 self.a =a5
6 classB(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 =b11 self.c =c12
13 defprintv(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()
View Code
总结:
作为好习惯,如果分类定义了__init__方法,要在子类的__init__中调用它。显式的调用
除非:子类没有定义__init__,会隐式去调用或者继承父类的__init__,子类一旦定义了__init__,就不会自动调用父类的init
如何正确的初始化:
1 classAnimal:2 def __init__(self, age):3 print('Animal')4 self.age =age5
6 defshow(self):7 print(self.age)8
9 classCat(Animal):10 def __init__(self, age, weight):#没有调用父类的init,这就导致没有实现继承效果
11 print('Cat')12 self.age = age + 1
13 self.weight =weight14
15 c = Cat(10, 5)16 print(c.__dict__)17 c.show()18
19 print('-' * 30)20 classAnimal:21 def __init__(self, age):22 print('Animal')23 self.age =age24
25 defshow(self):26 print(self.age)27
28
29 classCat(Animal):30 def __init__(self, age, weight):31 print('Cat')32 super().__init__(age)33 self.age = age + 1 #覆盖之前的self.age
34 self.weight =weight35
36
37 c = Cat(10, 5)38 print(c.__dict__)39 c.show()40
41
42 print('-' * 30)43 classAnimal:44 def __init__(self, age):45 print('Animal')46 self.age =age47
48 defshow(self):49 print(self.age)50
51
52 classCat(Animal):53 def __init__(self, age, weight):54 print('Cat')55 self.age = age + 1
56 self.weight =weight57 super().__init__(age)58
59
60 c = Cat(10, 5)61 print(c.__dict__)62 c.show()
注意init出现的位置
1 Cat2 {'age': 11, 'weight': 5}3 11
4 ------------------------------
5 Cat6 Animal7 {'age': 11, 'weight': 5}8 11
9 ------------------------------
10 Cat11 Animal12 {'age': 10, 'weight': 5}13 10
结果打印
例子中打印10 的,原因看__dict__ 就知道了,因为父类Animal的show方法中_age会被释放为_Animal__age,因此。显式10,而不是11,这样的设计不会,子类应该显式自己的属性值最好
解决办法:
一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类。