python面向对象编程——封装、继承、重写、MRO、多态

封装、继承、多态是面向对象编程的三大特征

9. 封装

  • 含义:

    • 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法,无需关心方法内部的具体细节,从而隔离了复杂度。
  • 在类对象内部通过访问控制把某些属性和方法隐藏起来,不允许在类对象的外部直接访问,而是在类对象的内部对外提供公开的接口方法以访问隐藏的信息,对隐藏的信息进行保护。

>>> class Student(object):
>>>     def __init__(self):
>>>         #隐藏属性
>>>         self.__score = 90
>>>     #提供接口
>>>     def get_score(self):
>>>         return self.__score
>>>     def set_score(self,score):
>>>         if 0 <= score <= 100:
>>>             self.__score = score
>>>         else:
>>>             raise ValueError("成绩在0-100之间") #抛出异常
>>> #输出90
>>> s = Student()
>>> print(s.get_score())
>>> 
>>> #输出88
>>> s.set_score(88)
>>> print(s.get_score())
90
80

10. 继承

当几个类对象中有共同的代码,为了提高代码利用率.
继承是实现代码复用的重要手段

继承的两个类别:
只继承了一个类叫做单继承,继承多个类叫做多继承

  1. 单继承:子类只有一个直接父类
    语法格式:
    class ChildClass(ParentClass)
    pass
  2. 多继承:子类有多个直接父类
    语法格式:
    class ChildClass(ParentClass1,ParentClass2,…)
    pass

注:
子类会继承所有父类(包括所有直接父类和间接父类)的所有属性和方法
子类可以添加父类中没有的方法和属性

>>> class Animal(object):
>>>     def eat(self):
>>>         print("吃饭")
>>>     def drink(self):
>>>         print("喝水")
>>> #继承Animal的属性和方法
>>> class Dog(Animal):
>>>     #添加父类中没有的方法
>>>     def swim(self):
>>>         print("游泳")
>>> dog = Dog()
>>> dog.eat()
>>> dog.drink()
>>> dog.swim
吃饭
喝水
游泳

11. 重写

  • 为什么要重写?
    如果子类对父类的某个属性或方法不满意,可以在子类中对其重写从而提供自定义的实现

  • 重写的方式:
    在子类中定义与父类中同名的属性或方法(包括装饰器)

注:

  1. 子类重写父类属性后,通过子类或其实力对象只能访问子类中重写的属性(或方法),无法访问父类中被重写的属性(或方法)
    2.重写后如何访问父类属性:
    父类中被重写的方法xxx(),在子类重写后的方法中可以通过super().xxx()进行调用
>>> class Parent(object):
>>>     #定义类属性
>>>     ca = "ca(父类)"
>>>     # 定义构造方法
>>>     def __init__(self):
>>>         print("__init__被调用了(父类)")
>>>     # 定义实例方法
>>>     def im(self):
>>>         print("im()被调用了(父类)")
>>>     # 定义类方法
>>>     @classmethod
>>>     def cm(self):
>>>         print("cm()被调用了(父类)")
>>> class Child(Parent):
>>>     ca = "ca(子类)"
>>>     def __init__(self):
>>>         # 调用父类被重写的方法
>>>         super().__init()
>>>         # 重写父类方法
>>>         print("__init__被调用了(子类)")
>>>     def im(self):
>>>         super().im()
>>>         print("im()被调用了(子类)")
>>>     @classmethod
>>>     def cm(self):
>>>         super().cm()
>>>         print("cm()被调用了(子类)")
>>> cc = Child()    
>>> print(Child.ca)
>>> print(cc.ca)
>>> cc.im()
>>> Child()
>>> cc.cm()
__init__被调用了(父类)
__init__被调用了(子类)
ca(子类)
ca(子类)
im()被调用了(父类)
im()被调用了(子类)
__init__被调用了(父类)
__init__被调用了(子类)
cm()被调用了(父类)
cm()被调用了(子类)

12. MRO 方法解析顺序

----Method Resolution Order

  • MRO指的是对于一颗类继承树,当调用最底层类对象所对应实例对象的方法时,python解释器在类继承树上搜索方法的顺序

  • 继承关系复杂时可以通过mro()或__mro__这个方法来查询获得这棵类继承树的MRO

  • 调用指定父类被重写的方法:
    可以给super()传入两个实参,obj对应类对象的MRO中,a_type后面那个类对象
    super(a_type,obj)
    a_type:类对象
    obj:实例对象

注:
从下到上,从左到右

>>> class A(object):
>>>     def f(self):
>>>         print("A.f")
>>> 
>>> class B(A):
>>>     def f(self):
>>>         print("B.f")
>>>         
>>> class C(A):
>>>     def f(self):
>>>         print("C.f")
>>>         
>>> class D(B,C):
>>>     #调用父类
>>>     def f(self):
>>>         super().f()   
>>> class E(B, C)
>>>     #调用指定父类
>>>     def f(self):
>>>         super(C,self).f()   
>>> d = D()
>>> d.f()
>>> e = E()
>>> e.f()
>>> print(D.mro())
>>> print(D.__mro__)
B.f
A.f
[, , , , ]
(, , , , )

13. 多态 – 在不考虑对象类型的情况下使用

  • 指未知变量所引用对象的类型,仍可以通过这个变量调用方法,在运行过程中根据变量引用对象类型,动态决定调用哪个对象中的方法
    注:如果子类中不存在指定名称的方法,回到父类中去查找,如果在父类中找到了,则调用父类中的方法
class ParentClass(object):
    def do_sth(self):
        print("do_sth() in ParentClass")

class ChildClass(object):
    def do_sth(self):
        print("do_sth() in ChildClass")

#子类不存在此方法将去父类查找
class ChildClass2(ParentClass):
    pass

def f(parent):
    parent.do_sth()
    
f(ParentClass())
f(ChildClass2())
#运行结果
do_sth() in ParentClass
do_sth() in ParentClass

python是动态语言,在调用函数时不会检查参数的类型

我们不关心变量parent引用对象是什么类型,只关心parent所引用对象是否有do_sth这个方法

你可能感兴趣的:(面向对象编程)