一:概念
面向对象(Object Oriented Programming OOP)编程思想主要是针对大型软件设计而来的;面向对象编程使程序的扩展性更强、可读性更好,使得编程可以更佳简单。
面向对象编程将数据和操作数据相关的方法封装到对象中组织代码和数据的方式更加接近人的思维,从而大大提高了编程的效率。
Python完全采用了面向对象的思想,是真正面向对象的语言,完全支持面向对象的基本功能,例如:封装,继承,多态等。
⚠️:Python支持面向过程,面向对象,函数式编程等多种编程范式
二:面向对象和面向过程的区别
2.1 面向过程(Procedure Oriented)思维
面向过程编程更加关注的是 ‘程序的逻辑流程’ ,是一种 ‘执行者’ 思维,适合编写小规模程序。
面向过程思想思考问题时,我们首先思考 ‘怎么按步骤实现?’ 并将步骤对应称方法一步一步,最终完成。这个适合简单任务,不需要过多协作的情况下。
比如,如何开车?我们很容易就列出实现步骤:
1.启动 --> 2.挂档 --> 3.踩油门 --> 4.走你
面向过程适合简单、不需要协作的事物;但当我们思考比较复杂的问题时,比如‘如何造车?’,就会发现列出1234步骤就不可能了,那是因为造车太复杂了,需要很多协作才能完成,此时面向对象思想就应运而生了。
2.2 面向对象(Object Oriented)思维
面向对象更加关注的是 ‘软件中对象之间的关系’ ,是一种 ‘设计者’ 思维,适合编写大规模的程序。
面向对象(Objcet)思想更加契合人的思维模式,我们首先考虑 ‘怎么设计这个事物?’;比如思考造车。我们就会先思考 ‘车怎么设计’,而不是 ‘怎么按步骤造车的问题’,这就是思维方式的转变。
三:面向对象和面向过程的总结
1. 都是解决问题的思维方式,都是代码组织的方式
2. 解决简单问题可以使用面向过程
3. 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程
四:类的定义
定义类的语法格式如下:
class 类名:
类体
要点如下:
1.类名必须符合 ‘标识符’ 的规则;一般规定,首字母大写,多个单词使用 ‘驼峰命名规则‘
2.类体中,我们可以定义属性和方法
3.属性用来描述数据,方法(函数)用来描述这些数据的相关操作
4.1 构造方法 __init__()
类是抽象的,也称为 ’对象的模版‘;我们需要通过类这个模版,创建出类的实例对象,然后才能使用类定义的功能。
我们前面说过一个Python对象包含三个部分:id(identity 识别码)、type(对象类型)、value(对象的值)。
现在,可以更进一步的说,一个Python对象包含:
1. id(identity 识别码)
2. type(对象类型)
3. value(对象的值)
(1)属性(attribute)
(2)方法(method)
创建对象,我们需要定义构造函数__init__()方法,构造方法用于执行 ‘实例对象的初始化工作’,即对象创建后,初始化当前对象的相关属性,无返回值。
__init__()方法要点如下:
1.名称固定:必须是__init__()
2.第一个参数固定:必须是self,self指的是刚刚创建好的实例对象
3.构造函数通常用来初始化实例对象的实例属性
4.通过类名(参数列表)来调用构造函数,调用后,将创建好的对象返回给相应的变量
5.__init__()方法:初始化创建好的对象,初始化指的是:给实例属性赋值
6.__new__()方法:用于创建对象,但我们一般无需重新定义该方法
4.2 实例属性
实例属性是从属于实例对象的属性,也称为 ‘实例变量’。它的使用有以下几个要点:
1.实例属性一般在__init__()方法中定义:
self.属性名 = 初始值
2.在本类的其他实例方法中,通过 ‘self.属性名’ 调用
3.创建实例对象后,通过实例对象访问:
obj1 = 类名 # 创建对象,通过__init__()初始化属性
obj1.属性名 = 值 # 可以给已有属性赋值,也可以新增属性
4.3 实例方法
实例方法是从属于实例对象的方法,定义格式如下:
def 方法名(self,形参列表...):
函数体
方法调用格式如下:
对象.方法名(实参列表...)
4.4 实例方法调用过程
4.5 类对象
4.6 类属性和类方法
4.6.1 类属性
类属性是从属于 ‘类对象’ 的属性,也称为 ‘类变量’;可以背所有实例对象共享
类属性的定义方式:
class 类名:
类变量名 = 初始值
在类中或类的外面,我们可以通过:‘类名.类变量名’ 来读写
4.6.2 类方法
类方法是从属于 ‘类对象’ 的方法;类方法通过装饰器@classmethod来定义,格式如下:
@classmethod
def 类名(cls,形参列表...):
函数体
要点如下:
1.@classmethod必须位于方法的上面一行
2.第一个cls必须有;cls就是指 ‘类对象’ 本身
3.调用类方法格式:‘类名.类方法名(实参列表...)’,参数列表中不需要也不能给cls传值
4.类方法中访问实例属性和实例方法会导致错误
5.子类继承父类方法时,传入cls是自类对象,而非父类对象
4.7 静态方法
Python中允许定义与 ‘类对象’ 无关的方法,称为 ‘静态方法’
‘静态方法’和在模块中定义的普通方法没有区别,只不过 ‘静态方法’ 放到了 ‘类的名字空间里面’,需要通过 ‘类调用’。
‘静态方法’ 通过装饰器@staticmethod来定义,格式如下:
@staticmethod
def 静态方法名(形参列表...):
函数体
要点如下:
1.@staticmethod必须位于方法的上面一行
2.调用静态方法格式:‘类名.静态方法名(实参列表...)’
3.静态方法中访问实例属性和实例方法会导致错误
4.8 __del__方法(析构函数)和垃圾回收机制
__del__方法称为析构函数,用于实现对象被销毁时所需的操作;比如:释放对象占用的资源、打开的文件资源,网络连接等
Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__方法。
我们也可以通过del语句删除对象,从而保证调用__del__方法。
系统会自动提供__del__方法,一般不需要自定义析构函数。
4.9 __call__方法和可调用对象
定义了__call__方法的对象,成为 ‘可调用对象’ ,即该对象可以像函数一样被调用
4.10 方法没有重载
在其他语言中,可以定义多个重名方法,只要保证方法签名唯一即可;方法签名包含三个部分:方法名、参数数量、参数类型。
Python中,方法的参数没有声明类型(在调用的时候才确定类型),参数的数量也可以由可变参数控制。因此,Python中是没有方法重载的。定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法重载。
如果我们在类体中定义了多个重名方法,只有最后一个有效。
4.11 方法的动态性
Python是动态语言,我们可以动态的为类添加新的方法,或者动态的修改类的已有方法
4.11 私有属性和私有方法
Python对于类的成员没有严格的访问控制限制,这与其他面向对象的语言有区别;私有属性和私有方法有如下要点:
1.通常我们约定,两个下划线开头的属性是私有的(private),其他为公开的(public)
2.类的内部可以访问私有属性和私有方法
3.类的外部不能直接访问私有属性和私有方法
4.类的外部可以通过 ‘_类名__私有属性(方法)名’,访问私有属性(方法)
⚠️:方法的本质也是属性,只不过可以通过 () 执行而已。
为什么可以通过 ‘_类名__私有属性(方法)名’访问呢?
我们看下本质:
因为私有属性就是以 ‘_类名__私有属性(方法)名’ 这种格式来存储到对象中的
4.12 @property 装饰器
@property 可以将一个方法的调用方式变成 ‘属性调用’
4.13 get与set方法
当我们声明了私有属性后,当想使用这个属性怎么办呢?这时就用到了set各get方法
五:面向对象的三大特征
1.封装(隐藏):隐藏对象的属性和实现细节,只对外提供必要的方法,相当于将 ‘细节封装起来’,只对外暴露 ‘相关调用方法’。
2.继承:继承可以让子类具有父类的特征,提高了代码的重用性;从设计上讲是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
3.多态:多态是指同一个方法调用由于对象不同会产生不同的行为。
5.1 继承
继承是面向对象程序设计的重要特征,也是实现 ‘代码复用’ 的重要手段。
如果新类继承一个设计好的类,就直接具备已有类的特征,就大大降低了工作难度;已有的类称为 ‘父类或基类’ ,新类称为 ‘子类或派生类’
Python支持多重继承,一个子类可以继承多个父类,语法格式如下:
class 子类名(父类1,父类2,...):
类体
如果在类定义中没有指定父类,则默认父类是object类;也就是说object类是所有类的父类,里面定义了一些所有类共有的默认实现,如:__new__()
定义子类时,必须在其构造函数中调用父类的构造函数,格式如下:
父类名.__init__(self,参数列表)
5.1.1 类成员的继承和重写
1.成员继承:子类继承了父类初构造方法之外的所有成员
2.方法重写:子类可以重新定义父类的方法,这样就会覆盖父类的方法
5.1.2 多重继承
Python支持多重继承,一个类可以有多个 ‘直接父类’;这样就具备了 ‘多个父类’ 的特点,同时也会破坏 ‘类的整体层次’,搞的异常复杂,谨慎使用。
5.2 多态(polymerrphism)
多态需要注意两点:
1.多态是方法的多态,属性没有多态
2.多态的存在有两个必要条件:继承,方法重写
六:object根类
object是所有类的父类,因此所有类都有object类的属性和方法
通过类的方法 mro() 或者类的属性 __mro__ 可以输出这个类的继承层次结构
6.1 dir()查看对象属性
查看自定义对象所有属性和object进行比对
可以看出Person对象多出了6个属性,子类Person包含了object类的所有属性
6.2 重写str()方法
__str__()方法用于返回一个对于 ‘对象的描述’,对应于内置函数 str() ,经常用于print() 方法,帮助我们查看对象的信息;__str__()可以重写
6.3 MRO()
Python支持多重继承,如果父类中有相同名字的方法,在子类中没有指定父类名字时,解释器将 ‘从左到右’ 按顺序搜索
MRO(Method Resolution Order):方法解析顺序,我们可以通过 mro()方法获取 ‘类的层次结构’,方法解析顺序也是按照这个 ‘类的层次结构’ 寻找的
6.4 super()获取父类的定义
在子类中,如果想要获取父类的方法时,可以通过super()来做。
super()代表父类的定义,不是父类的对象。
七:运算符的重载和特殊方法
Python运算符实际上是通过调用对象的特殊方法实现的,如:
常见的特殊方法统计如下:
每个运算符实际上都对应了相应的方法,统计如下:
特殊属性
Python对象中包含了很多双下划线开头和结尾的属性,这些事特殊属性,有特殊用法,这里列出几种常见的