导图社区 Python面向对象编程与用法演示
此思维导图是本人学习python所做的笔记,里面包含详细的语法规则,以及用法演示的截图,希望能给你带来帮助!
编辑于2022-05-21 17:29:16面向对象编程
零、名词
类 = 类对象
对象 = 实例对象
属性:形式是“变量名 = 值”
方法 = 实例方法(def函数,可理解为某些动作)
用类创建对象 = 用类实例化对象
类方法:def 类方法名(cls):
实例属性:def里面的属性(self.变量名),即实例对象的属性
类属性:def之外的属性(变量),类对象和实例对象共有的属性
一、面向对象基础
释义
面向对象是一种抽象化的编程思想,能够化简代码。面向对象就是将编程当成一个事物,对外界来说,事物是直接使用的,不用管内部的具体情况。而编程就是设置事物能够做什么事。
类和对象
类
类是对一系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物。
特征就是属性(变量)
行为就是方法(函数)
对象
对象是类创建出来的真实存在的事物。用类来创建对象后,则该对象会拥有该类当中的所有属性和方法。
二者关系
用类来创建(实例化)对象。
备注
类和对象是面向对象编程过程中的重要组成部分。
类和对象的语法
定义类
class 类名(): 代码块
备注
类名要满足标识符命名规则,同时遵循大驼峰命名习惯。
创建对象
对象名 = 类名()
调用实例方法
对象名.实例方法名()
备注
定义在类里面的函数一般称为实例方法或对象方法。
例
对象的属性 (实例属性)
类外面
类外面添加对象属性
对象名.属性名 = 值
类外面获取对象属性
对象名.属性名
例
类里面
类里面添加对象属性
self.属性名 = 值
例
备注
需要借助后面学的 __init__() 方法
要与后面学的类属性区别开来。 (类属性可以被类访问,实例属性不能)
类里面获取对象属性
self.属性名
例
备注
类里面的 self 指的是调用该函数的对象。
例
一个类创建多个对象
例
面向对象特性
封装
将属性和方法写到类里面的操作就是封装,封装可以为属性和方法添加私有权限。
继承
子类默认继承父类的所有属性和方法,子类可以重写父类属性和方法。
多态
传入不同的对象,产生不同的结果。
二、魔法方法
__init__()
语法
__init__(self)
备注
self (也可将self换其他字符)参数不需要手动传递,Python解释器会自动把当前的对象引用传递过去。
作用
初始化对象的初始属性。
注意
__init__() 方法在创建时默认被调用,不需要手动调用。
例
带参数
作用
一个类创建多个对象时,方便给对象设置不同的属性。
语法
__init__(self, 形参1, 形参2)
__str__()
作用
使用print打印对象时,默认输出的是对象的内存地址。但如果类定义了__str__() ,就会输出 return 后的数据(只能是 str 类型),也可输出其他代码块。这个魔法方法中一般放置的是解释说明性的文字,比如解释这个类的状态、作用等。
语法
__str__(self)
例
__del__()
作用
删除对象时,默认调用__del__() 方法。
语法
__del__(self)
注意
代码运行完时,内存会被释放,对象被删除,__del__() 方法会被自动调用。
例
return 返回值语句在 __del__() 将会失效。
魔法属性
对象.__dict__
作用
收集类对象/实例对象的属性或方法,以及对应的值,返回一个字典。
语法
类对象.__dict__
作用
返回类内部类属性和方法 (函数)及其对应的值组成的字典。
实例对象.__dict__
作用
返回实例属性和值组成的字典 (返回实例属性,即实例对象的属性)。
例
三、应用案例
炸鸡排
代码
购物
思路分析
用面向对象的思维,有多少个事物,就有多少个类。案例中应该有 商品类(商品属性有商品名、价格) 和 顾客类(顾客除了属性还有 pay 方法) 。
代码
注意
截图①中倒数第四行中的 item 表示传入的商品,商品有名称和价格两个属性,所以要用
四、继承
作用
子类默认继承父类的所有属性及方法。
一个类要想使用其它类的属性和方法,除了继承能实现,也能通过类中的“属性=其它类来”实现:
class 类名(父类名): 属性名 = 其它类名()
经典类和新式类
经典类 (旧式类)
Python2.0解释器默认类是按照经典类来解释的。
语法
class 类名: 代码块
新式类
Python3.0解释器默认类是按照新式类来解释的。
语法
class 类名(object): 代码块
备注
object 是所有类的父类,也叫基类,也可不写。
以后只写新式类。
单继承
释义
一个子类继承一个父类。
例
语法
class 父类名(object): 代码块 class 子类名(父类名): 代码块
多继承
释义
一个子类继承多个父类。
语法
class 父类1(object): 代码块 class 父类2(object): 代码块 class 子类名(父类1, 父类2): 代码块
备注
当继承情况很复杂时,继承路径太多,且Python代码是边解释边执行,只有执行到的时候才发现错误,所以应当避免多继承。
查看子类的父类
语法
print(子类名.__mro__)
例
或
print(子类名.mro())
多层继承
释义
多层继承是大于两层的继承。
语法
class AAA(object): 代码块 class BBB(AAA): 代码块 class CCC(BBB): 代码块
使子类调用父类方法和属性 (子类有同名属性和方法的情况下)
方法一
def 函数名(self): 父类名.__init__(self) 父类名.父类方法名(self) (self.属性名 = 父类属性的值)
备注
此方法是在子类里封装一个函数来调用父类初始化和父类函数。
最后一行括号的内容其实不用写,因为 父类名.__init__(self) 会将父类里 init 初始化里面的所有属性继承下来。
例
注意
在父(子)类的方法里,先调用自身的初始化,以防止init属性被覆盖。
缺点
①父类类名改变时,def函数里的父类名也要跟着改变。
②如果有多个父类,则代码量会冗余。
方法二
super()
写法一
def 函数名(self): super(当前子类名, self).__init__() super(当前子类名, self).要调用的函数() (self.属性名 = 父类属性的值)
写法二
def 函数名(self): super().__init__() (注:这句代码有时要写在父类里) super().要调用的函数() (self.属性名 = 父类属性的值)
这种super 没有self
例
备注
只会调用到子类的上一层父类,不能调用上面多层父类。
使用super()可以自动查找父类,调用顺序遵循__mro__类属性的顺序,比较适合单继承。
最后一行括号的内容其实不用写,因为 super().要调用的函数() 会将父类里 init 初始化里面的所有属性继承下来。
演示
注意
子类的 init 魔法方法也会默认覆盖父类的 init 魔法方法。
私有属性和方法
定义私有属性和方法
作用
设置某些实例属性或实例方法不继承给子类。
私有属性
self.__私有属性名 = 值
私有方法
def __私有方法名(self):
例
备注
即在属性名或方法名前面加上两个下划线“__”
访问和修改私有属性值
访问
访问函数习惯命名
get_私有属性名
备注
get_xx 只是工作习惯上的函数命名,换其他名称也不会报错。
语法
def get_私有属性名(self): return self.__私有属性名
修改
修改函数习惯命名
set_私有属性名
备注
set_xx 只是工作习惯上的函数命名,换其他名称也不会报错。
语法
def set_私有属性名(self): self.__私有属性名 = 值
注意
私有属性和私有方法只能在类里面访问和修改,而且要在父类里写入上面的代码,子类才能访问和修改。
例
调用私有方法
调用
语法
def get_私有方法名(self): return __私有方法名()
调用私有方法和访问私有属性的语法类似。
注意
私有属性和私有方法只能在类里面访问和修改,而且要在父类里写入上面的代码,子类才能访问和修改。
例
继承顺序 (mro)顺序
① 默认调用子类的属性和方法
若子类和父类有同名属性和方法(包括实例属性、实例方法、类属性、类方法),用子类创建对象,这个对象调用同名方法和属性时,默认调用到的是子类里的属性和方法。
② 再者调用第一个父类的属性和方法
当一个类有多个父类时,若子类使用两个父类的同名属性和方法,则默认使用第一个父类的。
③ 其次调用第二个父类的属性和方法
接上:当第一个父类没有该属性或方法时,才继承第二个类的属性或方法。
例
演示
说明:黑色实线代表继承关系,数字编号代表继承mro顺序。
五、多态
释义
一类事物有多种形态(一个抽象类有多个子类,因此多态的概念要依赖于继承,不过Python的多态不一定要依赖于继承,但最好依赖于继承)。子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。
优点
调用灵活,容易编写出通用的代码,以适应需求的不断变化。
步骤
定义父类,并提供公共方法(就算父类里定义一个空方法也可以,即方法里写pass)。
定义多个子类,并重写父类方法(每个子类的执行代码不一样)。
将不同子类的对象作为参数传递到另一个类,使不同的子类执行效果不同。
例
备注
将不同的兵器对象作为参数传入士兵对象里,执行的效果不同。
六、类属性、类方法
类属性
类属性 & 实例属性
实例属性是只属于对象的属性;类属性是类对象和实例对象共有的属性。 (每个实例属性都要占一份内存,因此对于数值不需要变的属性可定义为类属性,减少内存消耗)
例
定义类属性
语法
class 类名(父类名): 属性名 = 值
或
访问类属性
用类对象访问
类名.属性名
用实例对象访问
对象名.属性名
例
修改类属性
类名.属性名 = 值
例
注意
类属性只能通过类对象来修改,不能通过实例对象来修改。如果通过实例对象来修改,则表示创建一个同名实例属性。
其它
类中类
class 类名(父类名): class 类名: ...
类中的类也相当于类属性。
类中属性=其它类()
class 类名(父类名): 属性名 = 其它类名()
一个类要想使用其它类的属性和方法,除了继承能实现,也能通过类中的“属性=其它类来”实现。
类方法
使用场景
当方法中需要使用类对象(如访问类属性、私有类属性)时,定义类方法。
语法
@classmethod def 类方法名(cls): 代码块
例
备注
类方法的第一个参数必须是类对象,一般用cls来标识这个类对象。
类方法可通过 实例对象 和 类对象 来访问。
静态方法
使用场景
当类中的方法既不需要使用实例对象(如实例对象、实例属性),也不需要使用类对象(如类属性、类方法、创建实例等)时,可定义其为静态方法。 (取消不需要的参数传递,能减少不必要的内存占用和性能消耗。)
语法
@staticmethod def 静态方法名(): 代码块
例
备注
静态方法需要用装饰器“@staticmethod”来标识其为静态方法。
静态方法既不需要传递类对象,也不需要传递实例对象(形参没有self / cls)。
静态方法可通过 实例对象 和 类对象 来访问。
总结
实例属性、实例方法都是只属于对象的;类属性、类方法都是类对象和实例对象共有的,静态方法是只属于类的方法。
方法的总结
实例(对象)方法
可以处理对象属性、类属性
类方法
可以处理类属性(cls.属性 或 类.属性)
静态方法
可以处理类属性(类.属性)
七、异常
作用
当不确定某句代码是否会报错时,为了顺利执行下面的代码,可以将该语句放到异常语句里面,让异常语句来尝试执行该代码,如果该代码确实有误,则自动执行备选代码。
异常代码
语法
try: 可能有误的代码(块) except: 如果代码有误执行的代码(块)
例
备注
一般 try 下方只放一行代码。
捕获异常
捕获单个指定异常
语法
try: 可能有误的代码(块) except 异常类型: 如果捕获到该异常类型则执行的代码(块)
例
捕获多个指定异常
语法
try: 可能有误的代码(块) except (异常类型1, 异常类型2): 如果捕获到上述异常类型则执行的代码(块)
备注
要捕获多种异常类型时,要以元组的形式书写各异常类型。
捕获异常描述信息
语法
try: 可能有误的代码(块) except (异常类型1, 异常类型2) as 变量: print(变量)
例
捕获所有异常描述信息
语法
try: 可能有误的代码(块) except Exception as 变量: print(变量)
备注
Exception 是所有程序异常类的父类。
例
else / finally 语句
语法
try: 可能有误的代码(块) except: 代码有误时执行的代码(块) else: 没有异常时执行的代码 finally: 无论文件是否异常都执行的代码(块)
备注
finally 下方一般写关闭文件的操作。
else 和 finally 不一定要同时出现。
except 和 else 二者只执行其中一个。
“except:”也可改为“except Exception as info:”
例
拓展:cmd运行程序
用命令提示符运行.py文件
①用鼠标打开.py文件的文件夹。
②在文件夹的目录里输入“cmd”,按回车键
③在弹出框里输入解析器名和.py文件名。
备注
解析器名称要填解析器的文件名。
可以按“tab”键来补全输入的文件名。
嵌套异常语句
语法
try: ...... try: ...... except: ...... except: ......
嵌套异常传递顺序
先执行外层的异常语句,再执行内层的异常语句。
例
cmd 运行效果
自定义异常
作用
当程序的逻辑(不是语法)不符合用户的要求时,使程序报错。
步骤
1、自定义异常类
class 异常类(Exception): def __init__(self)
2、设置异常类的描述信息
def __str__(self): return ......
3、抛出异常类对象
raise 异常类创建的对象 / 异常类(参数)
4、捕获该异常
except Exception as 变量: print(变量)
备注
上面自定义的异常类继承了 Exception 。
或
try: raise Exception('提示信息') except Exception as 变量: print(变量)
例
例
八、模块 & 包
模块
释义
模块是一个.py文件(每个Python文件都可以作为一个模块),包含了Python对象定义和Python语句。 模块能定义函数、类、变量,也能包含可执行的代码。
模块文件位置
导入模块
方法一
import 模块
例
import 模块1, 模块2
备注
这种写法不符合PEP8规范
调用
模块.功能() / 模块.模块里的变量
方法二
from 模块 import 功能1, 功能2
例
from 模块 import *
例
备注
"*"这种方法会导入模块的所有功能。
调用
功能() / 模块里的变量
备注
这种导入方法,调用时无需写模块名。
方法三 (别名)
作用
当原有的模块名或功能名不方便记忆或使用时,可以定义别名。
注意
定义了别名之后,不能继续使用原名,要使用别名。
定义模块别名
import 模块名 as 别名
定义功能别名
from 模块名 import 功能 as 别名
例
推荐
推荐使用此方法,取别名后使用更简洁。
备注
import 后面都是跟模块或功能。
调用模块时模块内的执行顺序
先执行模块的函数外的代码,最后执行模块的函数内代码。
例
制作模块
作用
可以把实现某些功能的代码封装到自己定义的模块里,方便以后使用这些功能。
定义模块
注意
每个.py文件都可以作为一个模块,模块名就是文件名。也就是说,自定义模块名必须满足标识符命名规则。
测试模块
if __name__ == '__main__' 模块测试代码
备注
输入“main”再按回车即可自动补充写完。
if 后面的代码只在模块内部运行时才会执行。
__name__ 是.py文件的标识符,__name__ 在自身文件的值是__main__ ,在自身文件外的值是文件名(输出的文件名不包含.py后缀)。
例
调用模块
import 模块名 模块名.功能名(参数)
或
from 模块名 import * 功能名()
模块定位顺序
导入模块时,Python解析器对模块位置的搜索顺序是:当前目录 → shell变量PYTHONPATH下的每个目录 → 默认路径。模块搜索路径储存在system模块中的sys.path变量中,变量里包含当前目录、PYTHONPATH(解释器安装过程中选择的目录)、由安装过程决定的默认目录。
注意
自定义模块名不要和已有模块重名,否则已有模块会被自定义模块覆盖。
使用“ from 模块名 import 功能 ”的时候,如果功能名重复,则调用到的是最后定义或导入的功能。
当模块名和变量名重复时,二者会相互覆盖。 (二者都是通过引用来传递的,会被覆盖)
all列表
作用
如果一个模块中有__all__变量,当使用 " from 模块名 import * "导入模块时,只能导入all列表中的功能,而不会真的导入全部功能。但使用 " import 模块名" 导入则可真的导入全部功能。
__all__ = ['功能名1', '功能名2']
例
包
释义
包是装着有联系的模块以及一个名为 __init__.py 文件(可以是空文件)的文件夹。 (复杂的包需要init文件做一些初始设定)
制作包
右键 -【New】-【Python Package】- 输入包名 -【OK】- 新建功能模块(有联系的模块)
备注
新建包后,包内会自动创建 __innit__.py 文件,这个文件控制着包的导入行为。
例
导入、使用包
方法一
import 包.模块 包.模块.功能() / 包.模块.变量
例
例
from 包 import 模块1, 模块2 模块名.功能()
例
import 包.模块 as 变量1 变量1.模块里的变量
方法二
from 包 import * 模块名.功能() / 模块.模块里的变量
注意
用此方法前必须要先在 __innit__.py 文件中添加 __all__ = ['模块1', '模块2'] 来设置允许导入的模块,否则该模块不能通过 " * " 的方法导入。
from 包.模块 import * 功能() / 模块里的变量
例
备注
import 后面都是跟模块。
不建议用“ * ”方法导入模块,因为如果导入模块过多,会不知道变量或模块属性是从哪个模块导入的,不利于维护,也容易和自定义的属性或方法等的命名产生冲突。
案例-面向对象版学生管理系统
准备程序文件 (空白文件)
项目文件
角色文件
学员文件
管理系统文件
备注
为方便维护,一般一个角色一个文件。
主程序入口文件
项目要有主程序入口文件,习惯命名为 main.py。(后期要在main.py文件里导入角色文件)
项目目录
上述文件要放到同一个项目目录中。
目录创建步骤
【New】-【Directory】(文件夹)
目录结构
书写文件
学员文件
备注
管理系统文件实现的是对学员文件的操作,要先有学员才能对其进行操作,所以应该先书写学员文件的代码。
创建学员类
用 __init__ 初始化学员基本信息
用 str 魔法方法返回学员基本信息
代码
学员信息存储文件
设置一个空列表
备注
列表里的每个数据代表一个学员。
管理系统文件
创建管理系统类
导入学生类
定义存储学员信息的列表
定义 __init__ 魔法方法,用列表存储每个学生对象
定义程序的入口函数 (习惯命名为run函数)
从date文件中加载学员数据
显示功能菜单
提示用户输入功能序号
根据输入的序号来调用不同的函数
退出系统(其实是退出循环)
while 循环
定义系统的功能函数
从.date文件中加载学生信息
date文件内的数据是一个列表,列表内是多个字典,每个字典代表一个学生。 为了用面向对象思维来开发,需要将列表中的每个字典都转换为学生对象。 故需要遍历date里的列表中的每个字典,将每个字典的数据都传到Students()类中来生成多个学生对象。
显示系统功能菜单
备注
用静态方法
添加学员
删除学员
修改学员信息
查询单个学员信息
显示所有学员信息
保存数据到.date文件
此时的学生列表是一个存储着多个学生对象(打印时是内存地址)的列表,需要将这些对象都转为字典(需要使用“实例对象.__dict__”语句来实现)再存储到date文件中。
拓展
对象.__dict__
作用
收集类对象/实例对象的属性或方法,以及对应的值,返回一个字典。
语法
类对象.__dict__
作用
返回类内部类属性和方法 (函数)及其对应的值组成的字典。
实例对象.__dict__
作用
返回实例属性和值组成的字典 (返回实例属性,即实例对象的属性)。
例
书写模块测试代码
程序入口文件
作用
运行程序
设计模式
观察者模式
释义
观察者模式也叫 发布-订阅模式 ,这种设计模式定义了一种一对多的关系,让多个观察对象同时监听某一被观察者,当被观察者状态发生变化时,会通知所有观察者,使它们能够自动更新自己。
角色 (类)
被观察者
有状态的对象。观察者观察的对象。一般需要有注册和注销方法,用来添加和删除观察者。
观察者基类
这个类主要是需要定义一个接口,目的是被观察者发生变化时可以得到通知信息。
观察者
依赖者对象。这个类需要具体实现基类中的“通知”接口,使得和被观察者的变化保持同步。
通知方式
拉模型
重心在观察者上。当被观察者发生变化时,会广播所有的观察者,然后由观察者获取相应的数据。(观察者把数据拉进来)
推模型
重心在被观察者上。当被观察者发生变化时,被观察者将根据观察者的需要,将自身的变化推送给需要的观察者。(被观察者将消息推送出去)
优点
彼此交互的对象都是保持松耦合的。被观察者对观察者唯一的了解就是观察者实现的“通知”接口,除此之外它们之间都是互不影响且独立存在的,可以根据需要对自身作出修改。
可随时添加或删除观察者。
可以在很少甚至不修改被观察者或观察者的情况下进行对象之间高效的数据发送。
其他
观察者模式中是可以有多个被观察者和多个观察者之间的对应关系的,但是一定要弄清楚它们之间的关系以及变化,不然就会变得非常复杂。
一般情况是由被观察者来触发“通知”方法的,但是在特殊情况下也可以由观察者来触发“通知”方法。
代码
导入模块
from abc import abstractmethod
被观察者类
属性
被观察者名称
观察者列表
装着每个观察者对象(不是只装名字,是装整个对象)。
最新/历史动态
存储被观察者最新动态或储存被观察者所有历史动态。
方法
添加观察者
将观察者的名字传入,添加到观察者列表。
删除观察者
发起通知
发送消息,并遍历观察者列表,使观察者调用观察者自身的方法来调用被观察者的动态。
观察者类
属性
观察者名称
(绑定)被观察者
生成观察者对象的同时,将某个被观察者也作为参数传入,即生成观察者对象的同时,关注某个被观察者。或者不立即关注,只生成一个空的被观察者列表。
方法
添加被观察者
调用被观察者的添加观察者功能,将观察者自身的整个对象传过去(不只是传名字)。
删除被观察者
获取被观察者的动态
使被观察者调用自身的相关方法。
对通知做出反应
核心代码
被观察者类
属性
观察者列表
装着每个观察者对象。
方法
添加观察者
将观察者对象添加到列表。
(需要判断添加的对象是否已经在列表中)
删除观察者
(需要判断删除的对象是否已经在列表中)
通知观察者
将被观察者的动态作为参数传入该方法中,遍历观察者列表,使观察者调用观察者自身的方法来接收通知,并将被观察者的动态作为参数传过去。
观察者类
方法
接收通知
将被观察者的动态作为参数传入该方法中。
MVC
MVT
MVVM
单例
生产者消费者