Python-面向对象之单继承
类的继承
基本概念
面向对象三要素之一,继承inheritance,人类和猫类都是继承自动物 类。
个体继承自父母,继承了父母一部分特征,但也可以有自己的个性特征。
在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。
子类也可以自定义自己的属性和方法。
class Animal: def shout(self): print("Animal shouts") a = Animal() a.shout() class Cat: def shout(self): print("Cat shouts") c = Cat() c.shout() #输出 Animal shouts Cat shouts
上面2个类虽然有关系,但是定义是并没有这种关系,而且是各自定义,动物类和猫类都会叫,但是他们的叫法有区别,所以分别定义。
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 a = Animal("monster") a.shout() print(a.__dict__.items()) class Cat(Animal): pass cat = Cat('garfield') cat.shout() print(cat.name) print(cat.__dict__.items()) class Dog(Animal): pass dog = Dog("dongdong") dog.shout() print(dog.name) print(dog.__dict__.items()) #输出 Animal shouts dict_items([('_Animal__name', 'monster')]) Cat shouts garfield dict_items([('_Animal__name', 'garfield')]) Dog shouts dongdong dict_items([('_Animal__name', 'dongdong')])
上例可以看出,通过继承、猫类、狗类不用写代码,直接继承父类的属性和方法。
继承
class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表。
继承可以让子类从父类获取特征(属性和方法)
父类
Animal是Cat的父类,也称之为基类或超类。
子类
Cat就是Animal的子类,也称之为派生类。
定义
格式如下
class 子类名(基类1[基类2,.....]) 语句块
如果类定义时,没有基类列表等同于继承自object。在Python3中,object类是所有对象的根基类。
class A: pass #等价于 class A(object): pass
注意,上例中在Python2,写法是不同的。
Python支持多继承,继承也可以多级。
查看继承的特殊属性和方法有如下:
特殊属性和方法 含义
__bases__ 类的基类元组
__base__ 类的基类元组的第一项
__mro__ 显示方法的查找顺序,基类的元组
mro() 同上,返回列表
__subclasses__() 类的子类列表
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("Pig") a.shout() cat = Cat("cat") cat.shout() dog = Dog("Dog") dog.shout() print(Animal.__bases__) print(a.__class__.__bases__) print(Animal.__base__) print("-" * 30) print(Cat.__bases__) print(Cat.__base__) print(Cat.__mro__) print(Cat.mro()) print(int.__init_subclass__()) print(bool.mro()) #输出 Animal shouts Cat shouts Dog shouts (<class 'object'>,) (<class 'object'>,) <class 'object'> ------------------------------ (<class '__main__.Animal'>,) <class '__main__.Animal'> (<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>) [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>] None [<class 'bool'>, <class 'int'>, <class 'object'>]
Python2.2之前是没有共同的祖先,之后引入了object类,它是所有类的共同祖先类object。
新式类都是继承自object的,新式类可以使用super。
继承中的访问控制
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) @property def getweight(self): return self.__weight @classmethod def showcount1(cls): print(cls) print(cls.__dict__) print(cls.__COUNT) @classmethod def __showcount2(cls): print(cls.__COUNT) def showcount3(self): print(self.__COUNT) class Cat(Animal): NAME = "CAT" __COUNT = 200 @property def getcatcount(self): return self.__COUNT c = Cat(3,15,30) c.eat() print(c.HEIGHT) print(c.getweight) c._Animal__getweight() # 使用魔术方法访问 # print(c.__COUNT) #私有属性无法访问 # print(c._Cat__COUNT) # 200 # print(c._Animal__COUNT) # 101 print(c.getcatcount) c.showcount1() print("*" * 30) c._Animal__showcount2() print("*" * 30) c.showcount3() print(c.NAME) # CAT print(Animal.__dict__) print(Cat.__dict__) print(c.__dict__) print(c.__class__.mro()) #输出 Cat eat 30 15 15 200 <class '__main__.Cat'> {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, 'getcatcount': <property object at 0x0000016EA763C548>, '__doc__': None} 100 ****************************** 100 ****************************** 101 CAT {'__module__': '__main__', '_Animal__COUNT': 100, 'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x0000016EA76FCF28>, 'eat': <function Animal.eat at 0x0000016EA7710048>, '_Animal__getweight': <function Animal.__getweight at 0x0000016EA77100D0>, 'getweight': <property object at 0x0000016EA7606688>, 'showcount1': <classmethod object at 0x0000016EA7711080>, '_Animal__showcount2': <classmethod object at 0x0000016EA77110B8>, 'showcount3': <function Animal.showcount3 at 0x0000016EA77102F0>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None} {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, 'getcatcount': <property object at 0x0000016EA763C548>, '__doc__': None} {'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 15, 'HEIGHT': 30} [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
从父类继承,自己没有的,就可以从父类中找。
私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类的实例__dict__中,知道这个新的名称就可以访问到这个私有属性,魔术方法,慎用。
总结:
集成时,公有成员,子类和实例都可以随意访问;私有成员被影藏,子类和实例不可直接访问,但私有变量所在类内的方法中可以访问这个私有变量。
实例属性的查找顺序:
实例的__dict__ --> 类的__dict__-->如果有继承--> 父类的__dict__ --> object
# [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
如果搜索这些地方后没有找到就会抛异常,先找到就立即返回。
方法的重写、覆盖override
class Animal: def shout(self): print("Animal shouts") class Cat(Animal): def shout(self): #覆盖了父类的方法 print("Cat shouts") a = Animal() a.shout() cat = Cat() cat.shout() print(a.__dict__) print(cat.__dict__) print(Cat.__dict__) print(Animal.__dict__) #输出 Animal shouts Cat shouts {} {} {'__module__': '__main__', 'shout': <function Cat.shout at 0x000001925792F0D0>, '__doc__': None} {'__module__': '__main__', 'shout': <function Animal.shout at 0x000001925792F048>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
Cat能否覆盖自己的方法,可以。
Cat中能否对父类的方法做个增强,不需要完全重写,可以。
class Animal: def shout(self): print("Animal shouts") class Cat(Animal): def shout(self): print("Cat shouts") def shout(self): print(super()) print(super(Cat,self)) super().shout() # super(Cat,self).shout() #等价于super().shout() # self.__class__.__base__.shout(self) #等价上面两种写法,不推荐使用 print("maio miao miao") a = Animal() a.shout() cat = Cat() cat.shout() #输出 Animal shouts <super: <class 'Cat'>, <Cat object>> <super: <class 'Cat'>, <Cat object>> Animal shouts maio miao miao
super()可以访问到父类的类属性。
class Animal: @classmethod def class_method(cls): print('class_method_animal') @staticmethod def static_method(): print('static_method_animal') class Cat(Animal): @classmethod def class_method(cls): print('class_method_cat') @staticmethod def static_method(): print('static_method_cat') c = Cat() c.class_method() c.static_method() print(Cat.__dict__) print(Animal.__dict__) print(c.__class__.mro()) #输出 class_method_cat static_method_cat {'__module__': '__main__', 'class_method': <classmethod object at 0x0000010454AD6B38>, 'static_method': <staticmethod object at 0x0000010454AD6C88>, '__doc__': None} {'__module__': '__main__', 'class_method': <classmethod object at 0x0000010454AD6B00>, 'static_method': <staticmethod object at 0x0000010454AD6A90>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None} [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
类方法和静态方法是特殊的方法,也是类属性,这些方法都可以覆盖,属性字典的搜索顺序一致。
继承时使用初始化
class A: def __init__(self,a): self.a = a class B(A): def __init__(self,b,c): self.b = b self.c = c def printf(self): print(self.b) print(self.a) f = B(200,300) print(f.__class__.__bases__) print(f.__dict__) f.printf()
上例代码可知:
如果B定义是声明继承自类A,则在类B中__bases__中是可以看到类A
但是这个和是否调用类A的构造方法是两回事。
如果类B中调用了父类A的构造方法,就可以拥有父类的属性
class A: def __init__(self,a,d=10): self.a = a self.__d = d class B(A): def __init__(self,b,c): A.__init__(self,b+c,b-c) # super().__init__(b+c,b-c) self.b = b self.c = c def printf(self): print(self.b) print(self.a) f = B(20,30) f.printf() ##输出 20 50
作为好习惯,如果父类定义了__init__方法,你就应该在子类的init中调用它,子类如何自动调用父类的__init__?
示例1:
class A: def __init__(self): self.a1 = 'a1' self.b1 = 'b1' print('init A') class B(A): pass b = B() print(b.__dict__) #输出 init A {'a1': 'a1', 'b1': 'b1'}
B实例的初始化会自动调用基类的A的__init__方法
示例2:
class A: def __init__(self): self.a1 = 'a1' self.b1 = 'b1' print('init A') class B(A): def __init__(self): self.b1 = 'b1' print("init B") b = B() print(b.__dict__) #输出 init B {'b1': 'b1'}
B实例一旦定义了初始化__init__方法,就不会自动调用父类的初始化方法__init__,需要手动调用。
class A: def __init__(self): self.a1 = 'a1' self.b1 = 'b1' print('init A') class B(A): def __init__(self): A.__init__(self) self.b1 = 'b1' print("init B") b = B() print(b.__dict__) #输出 init A init B {'a1': 'a1', 'b1': 'b1'}
如何正确的初始化? 看如下示例
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): print('init in cat') self.age = age + 1 self.weight = weight c = Cat(10,5) c.show() #输出 init in cat 11
上例分析过,不会调用父类的__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 # super().__init__(age) #init in cat init in animal 10 self.weight = weight c = Cat(10,5) c.show() #输出 init in animal init in cat 11
注意,调用父类的__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): super().__init__(age) print('init in cat') self.__age = age + 1 # super().__init__(age) self.weight = weight c = Cat(10,5) c.show() print(c.__dict__) #输出 init in animal init in cat 10 {'_Animal__age': 10, '_Cat__age': 11, 'weight': 5}
上例中打印10,原因看__dict__就知道,因为父类Animal的show方法中的__age会被解释器改名为_Animal__age
解决办法:一个原则,自己的私有属性就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或子类的方法。
单继承
上面示例中,类的继承列表只有一个类,这种称之为单一继承。
OCP原则:多用继承,少修改
继承的用途,在子类上实现对基类的增强,实现多态。
多态
在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态。

浙公网安备 33010602011771号