封装
根据需求将属性和方法封装到一个抽象的类中
在使用的时候,只要按照自己的需求去调用,不必了解实现的细节
封装是面向对象编程的三大特征之一。
封装有两方面的含义:
1.将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样无需关心方法内部的具体实现细节,从而隔离了复杂度。
2.在类对象的内部通过访问控制把某些属性和方法隐藏起来,不允许在类对象的外部直接访问,而是在类对象的内部对外提供公开的借口方法(例如getter和setter)以访问隐藏的信息。这样就对隐藏的信息进行了保护。
![](https://i-blog.csdnimg.cn/blog_migrate/fe0cb5df20907f93487c73f88c37d712.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7ea0c9415166c4b5e491b35ecd98c1c7.png)
继承
当几个类对象中有共同的属性和方法时,就可以把这些属性和方法抽象并提取到一个基类中,每个类对象特有的属性和方法还是在本类对象中定义,这样只需要让每个类对象都继承这个基类,就可以访问基类中的属性和方法了。继承基类的每个类对象被称为派生类。基类也被称为父类或超类,派生类也被称为子类。
Python中的所有类对象都继承自一个统一的基类:object。这就是为什么我们在定义类对象时要在类名后面添加(object)。
除了封装,继承也是面向对象编程的三大特征之一。继承是实现代码复用的重要手段。
![](https://i-blog.csdnimg.cn/blog_migrate/dcccb8a78fa2bac2c45dc92387751868.png)
![](https://i-blog.csdnimg.cn/blog_migrate/98cae812a46b2d332588d3800820b27d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6060b9b5721d0ad7315cfb12646dabc6.png)
单继承:子类只有一个直接父类时称为单继承
假设子类和父类分别问ChildClass和ParentClass,子类继承父类的语法格式为:
class ChildClass(ParentClass):
pass
多继承:子类有多个直接父类时称为多继承
假设子类是ChildClass,直接父类时ParentClass1,ParentClass2,… ,ParentClassn
子类继承父类的语法格式为:
class ChildClass(ParentClass1, ParentClass2, ... ,ParentClassn):
pass
子类会继承所有父类(包括所有直接父类和所有间接)的所有属性和方法。
![](https://i-blog.csdnimg.cn/blog_migrate/152c87ff6abbcb415939faaa2f4c16f0.png)
子类可以添加父类中没有的属性和方法
![](https://i-blog.csdnimg.cn/blog_migrate/60273ad5595944de0bf00ffe4118df84.png)
重写(overwriting)
- 如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其进行重写从而提供自定义的实现,重写的方式为:在子类中定义与- 父类同名的属性或方法(包括装饰器)。
- 子类重写父类的属性后,通过子类或其实例对象只能访问子类中重写后的属性,而无法再访问父类中被重写的属性。
- 子类重写父类的方法后,通过子类或其实例对象只能调用子类中重写后的方法,而无法再调用父类中被重写的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/c9ee061239d534360fff79cd63a10cd9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bfb2dbf030affa8108a1faca0d5ade8c.png)
父类中被重写的名为xxx的方法,在子类重写后的方法中可以通过super().xxx()
进行调用。
![](https://i-blog.csdnimg.cn/blog_migrate/8cfe1f792b089b5c6d8d864df8ff118a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c1d46ad3814c2a4317e83a8cf98374a.png)
MRO
MRO的全称是Method Resolution Order(方法解析顺序),它指的是对于一棵类继承树,当调用最底层类对象所对应的实例对象的方法时,Python解释器在类继承树上搜索方法的顺序。
对于一颗类继承树,可以调用最底层类对象的方法mro()或访问最底层类对象的特殊属性__mro__,获得这棵类继承树MRO。
![](https://i-blog.csdnimg.cn/blog_migrate/e2809d5398e46a4e39330444edb7622a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/20a14644d64e34755290bbbbd271a878.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a914b6b93d2e8bc1570adf17c13041cc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5bac42ef8ba43f4c76eb6616e3d4bdaa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5e78b7b26c4c9d04333597a9d9761943.png)
在子类重写后的方法中通过super()调用父类中被重写的方法时,在父类中搜索方法的顺序基于以该子类为最底层类对象的类继承树的MRO。
![](https://i-blog.csdnimg.cn/blog_migrate/78e95d2dfed06933bc209721d459557d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bc2a2616b56c7b604bc11817d19f9263.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d145a7d106fbd3fdce200b6ee18ed759.png)
如果想调用指定父类中被重写的方法,可以给super()传入两个实参:super(a_type, obj)
,其中,第一个实参a_type
是个类对象,第二个实参obj
是个实例对象,这样被指定的父类是:obj
所对应类对象的MRO中,a_type
后面那个类对象。
![](https://i-blog.csdnimg.cn/blog_migrate/884b8e794be5e7e0460350f0959bf763.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6800c92c5535b78d3b28436b7801a151.png)
![](https://i-blog.csdnimg.cn/blog_migrate/490ed0a8871ae9c520306083322c8299.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8f8c1f2f23e90c28cc17a6a7c931110a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb80aa78c218ff2ef3d65cb66da182ac.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fbebe9064c6b1891f2e73ca2bce70f9c.png)
多态
多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态地决定调用哪个对象中的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/ae4becbca5cd9a48c3c650dfdeaaaadb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c4c67e8d43816a2c5965f66411981f8e.png)
如果子类中不存在指定名称的方法,回到父类中去查找,如果父类中找到了,则调用父类中的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/371272b66836ad60ad794423ab329616.png)
鸭子类型:
如果一只生物走起路来像鸭子,游起泳来像鸭子,叫起来也像鸭子,那么它就可以被当作鸭子。
也就是说它不关注对象的类型,而是关注对象具有的行为。
所以在上面的程序中,我们不关心变量parent所引用的对象是什么类型,到底是不是ParentClass或其子类类型,只关心变量parent所引用的对象是否有do_sth()这个方法。