Python面向对象继承
搬砖,赞路费 人气:0一 单继承
类继承作为python
的三大特性之一,在我们学习python的时候是必不可少的。使用类继承,能够大大减少重复代码的编写。现来记录下,python中关于类继承的一些知识点。
类的继承有单继承,多层继承以及多重继承,先来看看单继承。
1. 继承的基本语法格式如下
#类继承语法格式,B类继承A类 class A(): 类属性 类方法 ... class B(A): 类属性 类方法 ...
单继承的话一般类A是没有继承其他派生类的,只继承了基类。因为在python新式类中,一个类会默认去继承基类object的,基类object
是顶级类。
2. 查看类继承情况
class Father(): #这是父类 name1 = 'father_name' age1 = 'father_age' def father_method(self): print('我是父亲') class Son(Father): #这是子类 name2 = 'son_name' age2 = 'son_age' def son_method(self): print('我是孩子') if __name__ == '__main__': A = Father() B = Son() #单继承 print(B.__class__.__mro__) #或者Son.mro() print(Son.mro())
如上:我们定义了一个父类Father,一个子类Son,并且子类Son继承父类Father,它们都有自己的属性和方法。我们可以通过打印B.__ class__.__mro __ 或者Son.mro()来查看Son类的继承情况,如下:
>>> (<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>) [<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>]
可以看到,Son
类确实继承了Father
类,并且继承基类object。
3. 继承中的属性和方法
如果一个类继承了另外一个类,那么这个类是可以调用其继承类的属性和方法的(子类可以调用父类的属性和方法),如下
class Father(): #这是父类 name1 = 'father_name' age1 = 'father_age' def father_method(self): print('我是父亲') class Son(Father): #这是子类 name2 = 'son_name' age2 = 'son_age' def son_method(self): print('我是孩子') def fun1(self): #调用父类属性和方法 print(self.age1, self.name1) self.father_method() if __name__ == '__main__': A = Father() B = Son() #单继承 # print(B.__class__.__mro__) B.fun1()
结果如下:
>>>
father_age father_name
我是父亲
当子类中的属性名方法名和父类中的属性名方法名同名时,在该子类中会覆盖父类的属性名和方法名(重写)。
class Father(): #这是父类 name1 = 'father_name' age1 = 'father_age' def father_method(self): print('我是父亲') class Son(Father): #这是子类 name1 = 'son_name' age1 = 'son_age' def son_method(self): print('我是孩子') def father_method(self): #和父类方法同名,将以子类方法为准 print("与父类方法同名,但是我是子类") def son_fun1(self): #调用父类属性 print("子类属性和父类属性同名,以子类为准:", self.name1, self.age1) if __name__ == '__main__': A = Father() B = Son() #单继承 # print(B.__class__.__mro__) B.father_method() B.son_fun1()
输出如下:
>>>
与父类方法同名,但是我是子类
子类属性和父类属性同名,以子类为准: son_name son_age
4. 初始化函数__init__()和 super
上面写的子类和父类都是不需要传参数的,而当我们需要给类传参数时,往往都是要初始化的,下面来看看子类继承父类时,参数初始化的几种情况。
子类无新增参数:
class Father(): #父类 def __init__(self, name='张三', age=23): self.name = name self.age = age class Son(Father): #子类 def son_fun1(self): print(self.name, self.age) if __name__ == '__main__': B = Son() B.son_fun1()
输出:
>>>
张三 23
如上,在子类无新增参数时,无需进行__init__ 初始化,直接调用父类的对象属性即可。因为子类Son是继承自父类Father的,所以在调用时会首先去调父类的__init__ 进行初始化
子类有新增参数:
当子类有新增参数时,该怎么初始化呢?先来看看这个对不对
class Father(): #父类 def __init__(self, name='张三', age=23): self.name = name self.age = age class Son(Father): #子类 def __init__(self, height): self.height = height def son_fun1(self): print(self.height) print(self.name, self.age) if __name__ == '__main__': B = Son(170) B.son_fun1()
输出:
>>>
AttributeError: 'Son' object has no attribute 'name'
170
上面子类Son新增了一个height参数,然后用__init__ 进行初始化。但是从输出结果可以看出,height参数是正常打印的,打印name和age参数时就报错:子类Son没有属性’name’,因为这个时候就不会去调用父类的__init__ 进行初始化,而是直接调用子类中的__init__ 进行初始化,这时,子类初始化就会覆盖掉父类的__init__ 初始化,从而报错。
正确的初始化有两种方法,如下:
#方法1 def __init__(self, 父类参数1, 父类参数2, ..., 子类参数1, 子类参数2, ...) 父类名.__init__(self, 父类参数1, 父类参数2, ...) self.子类属性 = 子类属性 class Father(): #父类 def __init__(self, name='张三', age=23): self.name = name self.age = age class Son(Father): #子类 def __init__(self, name, age, height): #方法1 Father.__init__(self, name, age) self.height = height def son_fun1(self): print(self.height) print(self.name, self.age) if __name__ == '__main__': B = Son('李四', 24, 170) B.son_fun1()
>>>
175
李四 24
从结果可以看出,调用父类初始化后,结果就正常输出。这是在子类__init__
初始化的时候,调用了Father.__ init __(self, name, age)对父类对象属性进行了初始化。
现在来看看另外一个初始化方法super()
初始化,不过使用super()的时候要注意python的版本,因为python2和python3的使用是不同的,如下
#方法2 def __init__(self, 父类参数1, 父类参数2, ..., 子类参数1, 子类参数2, ...): #python2的super初始化 super(子类名, self).__init__(父类类参数1, 父类参数2, ...) self.子类属性 = 子类属性 #python3的super初始化 super().__init__(父类类参数1, 父类参数2, ...) self.子类属性 = 子类属性 class Father(): #父类 def __init__(self, name='张三', age=23): self.name = name self.age = age class Son(Father): #子类 def __init__(self, name, age, height): #方法2 #python2的super初始化 super(Son, self).__init__(name, age) self.height = height #python3的super初始化 # super().__init__(name, age) #或者 super(Son, self).__init__(name, age) def son_fun1(self): print(self.height) print(self.name, self.age) if __name__ == '__main__': B = Son('李四', 24, 175) B.son_fun1()
结果:
>>>
175
李四 24
上面使用的是super(Son, self).__ init __ (name, age)
来对父类对象属性进行初始化。不过这是要区分python版本的,在python3中使用super(Son, self).__ init__(name, age)或者super().__ init__(name, age)都是可以的,但是在python2的版本中,要使用super(Son, self).__ init__(name, age)来进行初始化,而且父类必须继承object,否则就会报错。
二 多层继承
上面的记录的是单继承,现在来看看多层继承。多层继承:子类继承自多个父类,父类只继承自object
顶级类。
class Father(): def __init__(self): print("enter father") print("leave father") def fun(self): print("这是father") class Mother(): def __init__(self): print("enter mother") print("leave mother") def fun(self): print("这是mather") class Son(Father, Mother): def __init__(self): print("enter son") super().__init__() print("leave son") if __name__ == '__main__': B = Son() B.fun() print(Son.mro())
输出:
>>>
enter son
enter father
leave father
leave son
这是father
继承关系:
[<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>]
这里有两个父类Father,Mother,一个子类Son,子类Son继承了类Father和Mother,继承顺序是Father在前,Mother在后。从上面输出结果来看,子类初始化过程是先进入子类Son的 __ init __方法,其次调用super(). __init __()进行父类对象属性初始化,最后初始化完成。
从结果可以知道上面super初始化调用的是父类Father的 __ init __方法。
。也可以看出,类Father和类Mother都有一个fun方法(同名),但在子类调用时调的是父类father的fun方法。这是为什么呢?
这里就涉及到super的继承机制,即super会根据MRO机制,从左到右依次调用父类的属性和方法, 当父类中有同属性名,同方法名时,以靠左的那个父类为准。
下面我们把Father和Mother位置换一下,如下:
class Father(): def __init__(self): print("enter father") print("leave father") def fun(self): print("这是father") class Mother(): def __init__(self): print("enter mother") print("leave mother") def fun(self): print("这是mather") class Son(Mother, Father):#这里变动,变换Father和Mother的位置 def __init__(self): print("enter son") super().__init__() print("leave son") if __name__ == '__main__': B = Son() B.fun() print(Son.mro())
结果:
>>>
enter son
enter mother
leave mother
leave son
这是mather
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
继承关系:
>>>
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]
可以看出,当Mother在前时,调用的就是类Mather的初始化方法和fun方法。所以在多层继承时,一定要注意父类的位置顺序。
三 多重继承
其实super的产生就是用来解决多重继承问题的,什么是多重继承呢?即,子类继承多个父类,而父类又继承自其它相同的类,这种又被称为菱形继承或者砖石继承,
如下:
A
/ \
/ \
B C
\ /
\ /
D
先来看看如下的一个菱形继承:
class A(): def __init__(self): print("enter A") print("leave A") class B(A): def __init__(self): print("enter B") # super().__init__() A.__init__(self) print("leave B") class C(A): def __init__(self): print("enter C") # super().__init__() A.__init__(self) print("leave C") class D(B, C): def __init__(self): print("enter D") B.__init__(self) C.__init__(self) # super().__init__() print("leave D") if __name__ == '__main__': d = D()
输出结果:
>>>
enter D
enter B
enter A
leave A
leave B
enter C
enter A
leave A
leave C
leave D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
上面用的是父类名. __ init__ () 来进行子类初始化的,但是从结果来看,类A的初始化方法是被执行了两次的,一次是类B的调用,一次是类C的调用。也可以看出子类D的初始化 __ init __ 调用了B.__ init __() 和C. __init __() 。现在D类继承的父类只有B和C,但是当代码比较复杂,继承的类比较多时,就得的一个一个写,如果类B和类C也是继承自多个类,那它们的初始化方法也得重新写,这样就比较繁琐,也容易出错。
下面来使用super初始化父类看看:
class A(): def __init__(self): print("enter A") print("leave A") class B(A): def __init__(self): print("enter B") super().__init__() print("leave B") class C(A): def __init__(self): print("enter C") super().__init__() print("leave C") class D(B, C): def __init__(self): print("enter D") super().__init__() print("leave D") if __name__ == '__main__': d = D() print(D.mro())
输出结果:
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
可以看到输出结果是不一样的,类A的初始化只执行了一次,而且当D类有新增父类时,初始化方法也不用动,从而避免重写初始化方法出错,代码也变得整洁。
这里还是和super的继承机制有关,上面说过当子类继承自多个父类时,super会根据MRO机制,从左到右依次调用父类的属性和方法,而且使用super初始化父类时,会一次性初始化所有的父类,所以上面的类A初始化方法不会被调用两次。
所以在类的多重继承中,一般建议使用super
来初始化父类对象属性。
加载全部内容