封装、继承、多态是面向对象编程的三大特征
含义:
在类对象内部通过访问控制把某些属性和方法隐藏起来,不允许在类对象的外部直接访问,而是在类对象的内部对外提供公开的接口方法以访问隐藏的信息,对隐藏的信息进行保护。
>>> 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
当几个类对象中有共同的代码,为了提高代码利用率.
继承是实现代码复用的重要手段
继承的两个类别:
只继承了一个类叫做单继承,继承多个类叫做多继承
注:
子类会继承所有父类(包括所有直接父类和间接父类)的所有属性和方法
子类可以添加父类中没有的方法和属性
>>> 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
吃饭
喝水
游泳
为什么要重写?
如果子类对父类的某个属性或方法不满意,可以在子类中对其重写从而提供自定义的实现
重写的方式:
在子类中定义与父类中同名的属性或方法(包括装饰器)
注:
>>> 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()被调用了(子类)
----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
[, , , , ]
(, , , , )
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这个方法