一、类的继承
面向对象三要素之一,继承Inheritance
人类和猫类都继承自动物类。
个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性。
在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样就可以减少代码、多服用。子类可以定义自己的属性和方法
class Animal: def __init__(self,name): self._name = name def shout(self): print(\"{} shouts\".format(self.__class__.__name__)) @property def name(self): return self._name class Cat(Animal): pass class Dog(Animal): pass a = Animal(\"monster\") a.shout() # Animal shouts cat = Cat(\"garfield\") cat.shout() # Cat shouts print(cat.name) # garfield dog = Dog(\"ahuang\") dog.shout() # Dog shouts print(dog.name) # ahuang
上例中我们可以看出,通过继承、猫类、狗类不用写代码,直接继承了父类的属性和方法
继承:
class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表。
继承可以让子类重父类获取特征(属性、方法)
父类:
Animal就是Cat的父类,也称为基类、超类
子类:
Cat 就是Animal的子类,也成为派生类
二、继承的定义、查看继承的特殊属性和方法
格式
class 子类 (基类1[,基类2,……]): 语句块
如果类定义时,没有基类列表,等同于继承自【object】。在Python3中,【object】类是所有对象基类
查看继承的特殊属性和方法
特殊属性 | 含义 | 示例 |
__base__ | 类的基类 | |
__bases__ | 类的基类元组 | |
__mro__ | 显示方法查找顺序,基类的元组 | |
mro() | 同上 | int.mro() |
__subclasses__() | 类的子类列表 | int.__subclasses__() |
三、继承中的访问控制
class Animal: __COUNT = 100 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) def showcount3(self): print(self.__COUNT) class Cat(Animal): NAME = \"CAT\" __COUNT = 200 #a = Cat() # TypeError: __init__() missing 3 required positional arguments: \'age\', \'weight\', and \'height\' a = Cat(30,50,15) a.eat() # Cat eat print(a.HEIGHT) # 15 #print(a.__COUNT) # AttributeError: \'Cat\' object has no attribute \'__COUNT\' #print(a.__showcount2) # AttributeError: \'Cat\' object has no attribute \'__showcount2\' #print(a.__getweight) # AttributeError: \'Cat\' object has no attribute \'__getweight\' a.showcount3() # 101 a.showcount1() # 100 print(a.NAME) # CAT print(Animal.__dict__) # {\'__module__\': \'__main__\', \'_Animal__COUNT\': 100, \'HEIGHT\': 0, \'__init__\': <function Animal.__init__ at 0x020DC228>, \'eat\': <function Animal.eat at 0x020DC468>, \'_Animal__getweight\': <function Animal.__getweight at 0x02126150>, \'showcount1\': <classmethod object at 0x020E1BD0>, \'_Animal__showcount2\': <classmethod object at 0x020E1890>, \'showcount3\': <function Animal.showcount3 at 0x021264F8>, \'__dict__\': <attribute \'__dict__\' of \'Animal\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'Animal\' objects>, \'__doc__\': None} print(Cat.__dict__) # {\'__module__\': \'__main__\', \'NAME\': \'CAT\', \'_Cat__COUNT\': 200, \'__doc__\': None} print(a.__dict__) # {\'_Animal__COUNT\': 101, \'age\': 30, \'_Animal__weight\': 50, \'HEIGHT\': 15}
从父类继承、自己没有的,就可以到父类中找
私有的都是不可访问的,但是本质上依然是改了名称放在这个属性所在的类的了【__dict__】中,知道这个新民成就可以了直接找到这个隐藏的变量,这是个黑魔法慎用
总结
继承时,共有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,当私有变量所在类内方法中可以访问这个私有变量
Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制
属性查找顺序:实例的【__dict__】——类的【__dict__】—–父类【__dict__】
如果搜索这些地方后没有找到异常,先找到就立即返回
四、方法的重写、覆盖override
class Animal: def shout(self): print(\"Animal shouts\") class Cat(Animal): def shout(self): print(\"miao\") a = Animal() a.shout() # Animal shouts b = Cat() b.shout() # miao print(a.__dict__) # {} print(b.__dict__) # {} print(Animal.__dict__) # {\'__module__\': \'__main__\', \'shout\': <function Animal.shout at 0x017BC228>, \'__dict__\': <attribute \'__dict__\' of \'Animal\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'Animal\' objects>, \'__doc__\': None}
Cat类中shout为什么没有打印Animal中shout的方法,方法被覆盖了?
这是因为,属性查找顺序:实例的【__dict__】——类的【__dict__】—–父类【__dict__】
那子类如何打印父类的同命的方法
super()可以访问到父类的属性
class Animal: def shout(self): print(\"Animal shouts\") class Cat(Animal): def shout(self): print(\"miao\") def shout(self): print(\"super(): \" , super()) print(super(Cat, self)) super().shout() super(Cat,self).shout() # 等价于super().shout() self.__class__.__base__.shout(self) #不推荐使用 a = Animal() a.shout() # Animal shouts b = Cat() b.shout() # super(): <super: <class \'Cat\'>, <Cat object>> # <super: <class \'Cat\'>, <Cat object>> # Animal shouts # Animal shouts # Animal shouts print(a.__dict__) # {} print(b.__dict__) # {} print(Animal.__dict__) # {\'__module__\': \'__main__\', \'shout\': <function Animal.shout at 0x019AC228>, \'__dict__\': <attribute \'__dict__\' of \'Animal\' objects>, \'__weakref__\': <attribute \'__weakref__\' of \'Animal\' objects>, \'__doc__\': None} print(Cat.__dict__) # {\'__module__\': \'__main__\', \'shout\': <function Cat.shout at 0x019F6150>, \'__doc__\': None}
super(Cat,self).shout()的作用相当于
调用,当前b的实例中找cat类的基类中,shout的方法
那对于类方法和静态方法是否也同样适用尼?
class Animal: @classmethod def class_method(cls): print(\"class_method\") @staticmethod def static_method(): print(\"static_methond_animal\") class Cat(Animal): @classmethod def class_method(cls): super().class_method() # class_method print(\"class_method_cat\") @staticmethod def static_method(cls,self): super(Cat,self).static_method() print(\"static_method_cat\") b = Cat() b.class_method() # class_method # class_method_cat b.static_method(Cat,b) # static_methond_animal # static_method_cat
这些方法都可以覆盖,原理都一样,属性字典的搜索顺序
五、继承中的初始化
看以下一段代码,有没有问题
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) a = B(100,300) print(a.__dict__) # {\'b\': 100, \'c\': 300} print(a.__class__.__bases__) # (<class \'__main__.A\'>,) a.printv() # 100 # AttributeError: \'B\' object has no attribute \'a\'
上例代码
如果B类定义时声明继承自类A,则在B类中__bases__中是可以看到类A
这和是否调用类A的构造方法是两回事
如果B中调用了A的构造方法,就可以拥有父类的属性了,如果理解这一句话?
class A: def __init__(self,a): self.a = a class B(A): def __init__(self,b,c): super().__init__(b+c) # A.__init__(self,b+c) self.b = b self.c = c def printv(self): print(self.b) print(self.a) a = B(100,300) print(a.__dict__) # {\'a\': 400, \'b\': 100, \'c\': 300} print(a.__class__.__bases__) # (<class \'__main__.A\'>,) a.printv() # 100 # 400
作为好的习惯,如果父类定义了__init__方法,你就改在子类__init__中调用它【建议适用super()方法调用】
那子类什么时候自动调用父类的【__init__】方法?
例子一:【B实例的初始化会自动调用基类A的__init__方法】
class A: def __init__(self): self.a1 = \"a1\" self.__a2 = \"a2\" print(\"A init\") class B(A): pass b = B() # A init print(b.__dict__) # {\'a1\': \'a1\', \'_A__a2\': \'a2\'}
例子二:【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\" self.__b2 = \"b2\" print(\"b init\") #A.__init__(self) b = B() # b init print(b.__dict__) # {\'b1\': \'b1\', \'_B__b2\': \'b2\'}
那如何正确实例化?
注意,调用父类的__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(Cat, self).__init__(age) print(\"Cat init\") self.age = age + 1 self.weight = weight a = Cat(10,5) a.show() # Animal init # Cat init # 11
怎么直接将上例中所有的实例属性改变为私有属性?
解决办法,一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即父类或者派生类
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(Cat, self).__init__(age) print(\"Cat init\") self.__age = age + 1 self.__weight = weight def show(self): print(self.__age) a = Cat(10,5) a.show() # Animal init # Cat init # 11 print(a.__dict__) # {\'_Animal__age\': 10, \'_Cat__age\': 11, \'_Cat__weight\': 5}
暂无评论内容