https://www.liaoxuefeng.com/wiki/1016959663602400/1017495723838528
这个博客记录一下学习廖雪峰的python课程,整理一下知识点。
1基本概念
面向对象编程OOP(Object oriented Programming)是一种程序设计思想,把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计是把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
假设我们要处理学生的成绩表,这个成绩表有两个属性,一个是学生姓名,一个是对应的成绩,用一个字典表示,
std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }
现在我们想打印学生的成绩:
如果采用面向过程的编程,那么我们就调用函数,打印学生成绩即可;
采用面向对象的程序设计思想,我们首先思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有两个属性,因为需要打印成绩,所以给这个Student类可以关联一个打印成绩的函数(在面向对象编程的时候称之为方法)。给对象发消息实际上是调用对象的关联函数,也就是对象的方法。
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student。
所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。
面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。
2类/实例/属性/方法
(黑色粗体为固定语法,无须变动;蓝色为需要根据实际需求调整的)
定义最简单的类:
class 类的名称(object):
pass
根据定义的类创建实例:
实例名称=类的名称()
给实例绑定属性:
可以自由地给实例绑定属性,如下
实例名称.属性名称=属性值/内容
由于类可以起到模板的作用,因此在创建实例的时候,可以把我们认为必须绑定的属性通过__init__这个特殊的方法强制写进去。
定义带属性的类
class 类的名称(object):
def __init__(self, 属性1名称,属性2名称,...):
self.属性1名称=属性1名称
self.属性2名称=属性2名称
根据带属性的类创建实例
实例名称=类的名称(属性1内容,属性2内容,...)
关于__iniit__的注意点
- __init__的第一个参数永远是self,表示创建的实例本身
- 有了__init__方法,在创建实例的时候,必须传入与__init__方法相匹配的参数,但self不需要传
定义带属性和方法的类
因为根据带属性的类所创建的实例本身就有属性的内容(数据),要访问这些内容(数据),就没必要从外面的函数去访问,可以直接在类的内部定义访问这些内容(数据)的函数,这样就把内容给封装起来了,这些封装数据的函数和类本身是关联起来的,我们称之为类的方法。
和普通函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是self,并且调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,仍然可以使用默认参数、可变参数、关键字参数和命名关键字参数。
class 类的名称(object):
def __init__(self, 属性1名称,属性2名称,...):
self.属性1名称=属性1名称
self.属性2名称=属性2名称
def 方法1名称(self):
print(self.属性1名称, self.属性2名称)
def 方法2名称(self):
if self.属性1名称>self.属性2名称:
return 'big'
根据带属性和方法的类创建的实例
实例名称=类的名称(属性1内容,属性2内容,...)
其形式与根据带属性的类创建实例一样。
这样通过属性把内容(数据)封装,通过方法把逻辑(函数)封装,调用很容易,但却不用知道内部实现的细节。
小节
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据互相独立,互不影响;
方法就是与实例绑定的函数,和普通函数不同的是,方法可以直接访问实例的数据;
通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节;
和静态语言不同,python允许对实例绑定任何数据(实例名称.新属性名称=新属性内容),也就是说,对于同一个类的两个不同实例,拥有的属性可能是不同的。
3继承和多态
在面向对象的程序设计中,最重要的三个特性就是,封装,继承和多态,前面我们已经了解了封装,接下来我们了解一下继承和多态。廖雪峰大神的例子非常好,这里就直接借用了
当我们通过
class 类的名称(object):
...
来定义类的时候,认为这个类是继承自object类(这是所有的类都会继承的类)。
现在假设我们定义了一个动物类,Animal
class Animal(object):
def run(self):
print('Animal is running...')
Animal类具有run这个方法。
当我们需要写一个猫类和狗类的时候,因为猫类和狗类也需要跑,所以在猫类和狗类中可以定义一个run方法
class Cat(object):
def run(self):
print('Cat is running...')
class Dog(object):
def run(self):
print('Dog is running...')
此时,Cat类和Dog类都有run这个方法,一切都没什么问题,猫和狗各自跑各自的,只是麻烦了一点,因为猫类狗类和动物类的跑的方法是一样的。事实上,我们把Animal类可以作为Cat类和Dog类的父类,这样Cat类和Dog类就能继承Animal类的run方法了。(喊一声爸爸就能轻松躺赢)
子类想从父类继承也非常简单,只需要把object换成你想要继承的类的名称就可以了,这样子类就获得了父类的全部功能。当然也可以对子类增加一些新方法。
子类继承父类的功能
class 子类的名称(父类的名称):
def 新方法名称(self):
...
子类改写父类的功能
当子类继承了父类的功能,但是想在继承的功能上做修改,这也是可以的
class 子类的名称(父类的名称):
def 父类方法名称(self):
改写的功能
此时,子类和父类都存在着相同的方法,在运行时,要看具体的实例是根据子类还是父类定义的,如果是根据子类定义的,那么子类的方法会覆盖父类的方法,也就是说会运行改写的功能。这就是多态
小结:
继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。
4静态语言和动态语言
对于静态语言(如java)来说,如果一个函数需要传入某个类,则传入的对象必须是这个类或者它的子类;
而对于python这样的动态语言,则不一定需要传入这个类,我们只需要保证传入的类有所需要调用的方法即可。
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子