面向对象(OOP)
面向对象三大特性:封装;继承;多态
面向过程:
把完成某一个需求的所有步骤从头到尾逐步实现,对功能独立的代码封装成函数,最后程序的实现就是不断的调用不同的函数
特点: 注重步骤和过程,需求复杂,代码复杂
面向对象:
确定职责(方法)——确定对象——就是让不同的对象调用不同的方法
特点: 注重对象和职责;适合复杂项目
封装
封装
封装:根据职责将属性和方法封装到一个抽象的类中
类:抽象的,是用来创造对象的 ——图纸
对象:具体的,由类创建出来的一个具体的存在。可以直接使用 ——实物
关系:先有类后有方法;类中定义了什么样的属性和方法,对象中就有什么样的属性和方法。特征被称为属性 行为被称作方法;由哪一个类创建出来的对象,就拥有哪一个类中定义的属性和方法
类的三要素:
- 类名:类的命名规则:每个单词首字母大写;单词与单词之间没有下划线
- 属性:类的事物具有什么样的特征(名词)
- 方法:类的事物具有什么样的行为(动词)
格式:
定义类:
class 类名:
def 方法1(self,参数列表)
pass
def 方法2(self,参数列表)
pass
注意实例方法第一个参数是self
方法的定义格式与函数定义的格式一样
引用的概念:
等号右侧负责创建对象,等号左侧的变量负责对这个对象进行引用
在面向对象开发中,引用的概念是同样适用的;在使用类创建对象之后,使用变量去接收,并且变量中记录的是对象在内存中的地址,使用print函数输出对象变量时,输出的是对象由哪一个类创建的对象,以及在内存中的地址(十六进制)同一个类创造的不同对象是不同的,内存地址不同。
self
类的属性要在类的内部定义
self代表的是类的实例,而不是类,并且它可以取得该类的所有方法和变量;同一个类下不同的方法可以相互调用:self.fun();定义类的方法中,类中方法的第一参数必须是self本身,定义时不可省略,传参时可以省略
self的使用;哪一个对象调用的方法,self就是哪一个对象的引用;def eat(self)——print(“%s”%self.属性)
注意:在类封装的方法内部,self就表示当前调用方法的对象自己。调用方法时,不需要传递self参数,在定义时,第一个参数必须是self;在方法内部,可以通过self访问对象属性,也可以调用其他对象的方法
为何必须在方法定义中包含形参 self 呢?因为Python调用这个 __init__() 方法来创建 Dog 实例时,将自动传入实参 self 。每个与类相关联的方法调用都自动传递实参 self ,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。当我们创建实例时,传递实参的时候,self 会自动传递。因此只需要传递除self以外的参数。以 self 为前缀的变量都可供类中的所有方法使用,我们可以通过类的任何实例来访问这些变量。某某类的属性就是创建实例的时候传递的实参,通过实例访问的变量称为属性——形参,即:通过初始化参数创造出来的self.xxxx就是类的属性;有些默认属性,即使没有通过实例传入实参,但是在初始化参数中也有设置,这个也作为属性。
初始化方法
当使用类名()创建对象时,会自动执行以下操作:
- 内存中分配空间——创建对象
- 对属性设置初始值——初始化方法(init);即自动调用__init__
- __init__方法是专门用来定义一个类具体具有哪些属性的方法
用初始化方法内部定义属性:
具体方法:在方法内部使用self.属性名=属性的初始值(传递的形参)
在创建对象的时候,类名参数里面赋值
所谓方法,就是封装在类里面具体的函数;具体的语法和之前学习的函数一样
改造初始化方法-——初始化的同时设置初始值
- 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对_ init_ 方法进行改造。
- 把希望设置的属性值,定义成__init__方法的参数
- 在方法内部使用self.属性=形参接收外部传递的参数
- 在创建对象时,使用类名(属性1, 属性2...)调用
私有属性和私有方法
利用方法可以调用私有属性;并且在对象的方法内部,是可以访问到对象的私有属性的
应用场景:对象的某些属性或方法只希望在对象的内部被使用,不希望在外部被访问到
定义方式:在属性名或者方法前加两个下划线
伪私有属性 私有方法:
python中没有真正意义的私有;python中进行了特殊处理:_类名__属性名;_类名__方法名
继承
继承
继承:实现代码的重用;即:相同的代码不需要重复的编写;子类只需要根据职责封装子类特有的属性和方法
继承的概念:子类拥有父类的所有属性和方法
基本语法:
class 类名(父类名):
pass
专业术语:
子类——父类——子类从父类继承
派生类——基类——派生类从基类派生
即:继承=派生
继承的特性——传递性
c类继承b类;b类继承a类 即:c类拥有a,b所有的属性和方法
方法的重写:首先要定义子类的方法,在子类方法中重用父类方法
- 父类的方法实现与子类完全不同:如果在子类中,重写了父类的方法,在使用子类对象调用的方法时,会默认调用子类的方法。
- 子类的方法实现包含有父类的方法实现:对父类方法进行扩展。
- 在子类中重写子类的方法,在需要的位置使用super().父类方法(参数)来调用父类方法执行。这样通过super将父类和子类关联起来,从而使得子类方法包含父类方法的所有属性;super() 函数就是让他们联系在一起。
- 在子类的对象方法中,不能直接调用父类的私有方法和私有属性。子类可以通过访问父类的共有方法间接访问父类的私有属性和方法
多继承
多继承:一个子类拥有多个父类
class 子类名(父类名1,父类名2……………….)
pass
注意:如果父类之间存在重名的属性或方法的时候,避免使用多继承,一般程序自动执行第一个父类名。
MRO:method revolution order 方法搜索顺序:
python中针对类提供了一个内置属性,__mro__可以查看方法搜索顺序
主要用于在多继承中判断方法,属性调用的路径
确定某一类中的调用属性,方法的顺序:print(类名,__mro__
object是python中为所有对象提供的基类。提供一些内置属性和方法,可以通过dir(对象)函数查看
新式类:以object为基类
旧式类:不以object为基类
所以建议在没有父类的时候,统一在括号内加上object
多态
多态
不同的子类对象,调用相同的父类方法,产生不同的执行结果
多态增加代码灵活度;并且以继承和重写父类为前提
多态仅仅是调用方法的技巧,不会影响到类的内部设计
例如:定义狗;哮天犬,两者都拥有游戏的方法。定义人类,传入不同的狗,有不同的游戏,这就是多态
类属性;类方法
类属性;类方法
每一个对象都有自己独立的内存空间,保存自己各自不同的属性
多个对象的方法在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
定义的类属于类对象,类对象有属于自己的属性和方法:类属性;类方法;通过类创造出来的对象属于实例对象,在程序运行时,类会被加载到内存
类属性:在class下方,通过赋值语句进行定义。
类属性只会记录跟类有关的特征;不会记录跟对象有关的特征
python中的属性获取机制:
- 首先在对象内部查找对象属性
- 没有找到的话,会向上寻找类属性
访问类属性的两种方式:
- 类名.类属性(推荐)
- 对象名.类属性 但是要注意,这样只会给对象添加一个属性,而不会影响类属性的值
类方法的定义:
@classmethod //修饰器
def 类方法名(cls)
pass
类方法的第一个参数必须是cls
在类方法中,要想访问当前类的属性——cls.类属性
调用类方法:类名.方法名(),此时不用出传递参数,也不用创建对象
静态方法的定义:
@staticmethod //修饰器
def 静态方法名()
pass
调用:类名.静态方法名();不需要创建对象
类方法;静态方法;实例方法说明
- 访问实例属性——实例方法;实例方法——方法内部需要访问实例属性;实例方法内部可以使用类名.访向类属性;调用时实例方法加self
- 访问类属性——类方法;类方法——方法内部只需要访问类属性;调用时类方法加cls
- 既不访问实例属性,也不访问类属性——静态方法;静态方法——方法内部不需要訪何实例属性和类属性;调用时静态方法不加self
单例设计模式
设计模式:前人总结问题的设计方法(套路)
目的:让类创建的对象在系统中只有唯一一个实例
每一次执行类名()返回的对象,内存地址是相同的
内置方法:格式:__XXXX__
__new__方法:
在使用类名()创建对象时,首先会调用__new__方法,这是一个由object基类提供的内置静态方法,主要作用为:
- 在内存中为对象分配空间
- 返回对象的引用
解释器获得对象的引用之后,将引用作为第一个参数传递给__new__方法
new 方法的使用:
重写new方法时,一定先为对象分配空间:super().__new__(cls)
再返回对象的引用:return super().__new__(cls) 因为new是静态方法,所以要加cls
通过new方法实现单例设计模式:
- 定义一个类属性——用于单例设计对象的引用(初始值是None)
- 重写new方法,调用父类方法,为第一个对象分配内存空间。每次返回的是类属性中记录的对象引用
- 引用实际上就是一个内存地址
初始化动作——只执行一次
定义一个类属性——init_flag=Flase
我们限制不了初始化方法,但可以限制初始化动作
异常
异常
抛出异常:程序停止执行并提示错误信息的动作称之为抛出异常
捕获异常:代码出错,解释器会抛出异常。我们可以进行捕获异常。
在程序开发时,很难将所有情况处理的面面俱到,通过异常捕获可以针对突发事件做集中性的处理,从而保证程序的稳定性和鲁棒性。
捕获异常用法(针对某些代码不一定能够执行得正确):
根据错误类型捕获异常
try:
尝试执行
except xxxxxxxxx:
出现错误的处理
xxxxxxxx:指的是解释器抛出异常的最后一个单词
捕获未知错误:
except Exception as result:
print(“未知错误%s” %result)
异常的完整语法:
try:
捕获异常:
except (错误类型1):
except (错误类型2):
except (错误类型3):
else: 没有异常的时候执行
finally:无论有没有异常都会被执行
异常的传递
当函数方法出现异常,将异常传递给函数/方法的调用一方。传递到主函数时仍然没有被处理,程序终止。
作用:利用异常的传递性,在主程序捕获异常,保证代码的整洁。
主动抛出异常:我们对于条件的限制,当得到的结果不符合实际规则,但是满足语法规则时,我们进行主动抛出异常。
创建异常对象:python中存在异常类(Exception)
对象名= Exception(”错误类型”)
主动抛出异常:raise关键字——raise 异常对象
在主程序中,进行异常捕获——except Exception as result:
—— print(“错误类型%s” %result)
模块和包
模块和包
模块是Python程序架构的一个核心概念
- 每一个以扩展名py结尾的Python源代码文件都是一个模块
- 模块名同样是一个标识符
- 在模块中定义的全局变量、函数、类都是提供给外界直接使用的工具。通过模块名.的方式。
- 模块就好比是工具包,要想使用这个工具包中的工具,就需要先导入这个模块
- 每次导入一个模块,应该独占一行
import 模块名1
import 模块名2
模块别名: import 模块名1 as 模块别名 (别名遵循大驼峰命名法)
导入一部分:
from 模块名 import 工具名
优势:不需要通过模块名.的方式,可以直接使用模块提供的工具。
注意:
- 如果两个模块重名,则后导入的会覆盖之前导入的,此时可以利用as起别名。
- 如果工具名为*,则直接使用所有工具,但是不推荐使用。
模块的搜索顺序:
搜索当前目录指定的模块名文件,有的话直接导入,否则,搜索系统目录。所以给文件起名字的时候不要和系统的模块文件名重名。每个模块都有一个内置属性:__file__可以查看模块的完整路径
在导入文件时,所有没有任何缩进的代码都会被执行一遍
注意:我们使用import工具时,工具只包含全局变量;函数;类而直接执行的代码不是向外界直接提供的工具;一般情况下,不允许擅自缩进!
__name__:
这是一个内置属性,记录着一个字符串
如果是其他文件导入的——__name__就是模块名
如果是当前执行的程序——__name__就是__main__:
应用:def xxxx():
pass
if__name__==”__main__”
xxxx()
这样每一个文件都可以作为一个直接可以导入的模块
包(package):
包是一个包含多个模块的特殊目录,并且该目录下有一个特殊的文件:__init__.py。命名规则与变量一样,使用小写字母_XXXX
优势:一次性导入所有模块
创建过程:先创建directory,再创建一个__init__.py
直接new package
如果想在外界使用包中的模块,需要在__init__.py中指定外界提供的模块列表
语法: from. import 模块列表
发布模块:将包制作成压缩包——自己开发——分享给别人
- 建立新文件夹——创建setup.py文件以及包文件
- 构建模块(不在pycharm中实现)
python3 setup.py build
- 生成发布压缩包(不在pycharm中实现)
python3 setup.py sdist
安装模块:
删除模块:
文件
文件
文本文件:可以使用文本编辑软件打开——实质上还是二进制文件
二进制文件:保存的内容不是给人直接阅读的,而是提供给其他软件使用的
例如:图片,音频,视频等,不能使用文本编辑软件打开
两者的本质都是二进制文件
计算机中操作文件的步骤:
打开——读写——关闭
在python中
打开: file=open(“文件名”)
读取: file.read() 默认读取所有
关闭: file.close()
文件指针:标记从哪个位置开始读取数据
在第一次打开文件时,文件指针指向文件最开始的位置,在执行了read方法之后,文件指针会移动到读取内容的末尾
打开文件方式:
注意:频繁的移动文件指针,会影响文件读写的效率,所以开发中更多的时候以只读或者只写的方式来操作文件。带+的一般不用。
read方法在面对文件太大时,对内存占用在过于严重。
readline方法:一次读取一行内容;方法执行后,会把指针移动到下一行,
准备再次读取。
在使用readline的时候,通常配合while:
while True:
**********
if not test:
break
文件复制:
file_read=open(“readme”)
file_write=open(“readme1”,”w”) ——只写方式
文件/目录常用的管理操作
在终端/文件浏览器中,可以实现对文件/目录的管理操作。此时需要导入os模块:
eval函数:将字符串当成有效的表达式来求值并返回计算结果
变量,数据,函数都是对象
面向对象基本语法
通过内置函数dir传入标识符/数据,可以查看对象内的所有属性和方法