Python面向对象

Python面向对象

01 面向对象(OOP)基本概念

1.1 面向对象基本概念

  • 我们之前学习的编程方式就是面向过程
  • 面向过程面向对象,是两种不同的编程方式
  • 对比面向过程的特点,可以更好地了解什么是面向对象

1.1.1 过程和函数

  • 过程是早期的一个编程概念
  • 过程类似于函数,只能执行,但是没有返回值
  • 函数不仅能执行,还可以返回结果

1.1.2 面向过程和面向对象基本概念

1) 面相过程 —— 怎么做?

  • 把完成某一个需求的所有步骤从头到尾逐步实现
  • 根据开发需求,将某些功能独立的代码封装成一个又一个函数
  • 最后完成的代码,就是顺序地调用不同的函数

特点

  • 注重步骤与过程,不注重职责分工
  • 如果需求复杂,代码会变得很复杂
  • 开发复杂项目,没有固定的套路,开发难度很大!
    在这里插入图片描述
    2) 面向对象 —— 谁来做?
  • 相比较函数,面向对象更大的封装,根据职责在一个对象封装多个方法
    • 1.在完成某一个需求前,首先确定职责 —— 要做的事情(方法)
    • 2.根据职责确定不同的对象,在 对象内部封装不同的方法(多个)
    • 3.最后完成的代码,就是顺序地让不同的对象调用不同的方法

特点

  • 注重对象和职责,不同的对象承担不同的职责
  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
  • 需要在面向过程基础上,再学习一些面向对象的语法

02 类和对象

2.1 类和对象的概念

2.1.1 类

  • 是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
    • 特征被称为属性
    • 行为被称为方法
  • 是一个模板,是负责创建对象的

2.1.2 对象

  • 对象由类创建出来的一个具体存在,可以直接使用
  • 哪一个类创建出来的对象,就拥有在哪一个类中定义的:
    • 属性
    • 方法
  • 在程序开发中,应该先有类,再有对象

2.2 类和对象的关系

  • 类是模板对象是根据这个模板创建出来的,应该先有类,再有对象
  • 只有一个,而对象可以有很多个
    • 不同的对象之间属性可能会各不相同
  • 中定义了什么属性和方法对象中就有什么属性和方法,不可能多,也不可能少

2.3 类的设计

  • 在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!
  • 在程序开发中,要设计一个类,通常需要满足一下三个要素
    • 类名这类事物的名字,满足大驼峰命名法
    • 属性这类事物具有什么样的特征
    • 方法这类事物具有什么样的行为
  • 大驼峰命名法
    • 每一个单词的首字母大写
    • 单词与单词之间没有下划线

2.3.1 类名的确定

  • 名词提炼法分析整个业务流程,出现的名词,通常就是找到的类

2.3.2 属性和方法的确定

  • 对象的特征描述,通常可以定义成属性
  • 对象具有的行为(动词),通常可以定义成方法
  • 提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑

03 面向对象基础语法

3.1 定义简单的类

  • 面向对象更大封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!

3.1.1 定义只包含方法的类

  • Python 中要定义一个只包含方法的类,语法格式如下:
class 类名:

    def 方法1(self, 参数列表):
        pass
    
    def 方法2(self, 参数列表):
        pass
  • 方法的定义格式和之前学习过的函数 几乎一样
  • 区别在于第一个参数必须是 self,具体看后面介绍

注意类名命名规则要符合大驼峰命名法

3.1.2 创建对象

  • 当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()

在这里插入图片描述

  • 在面向对象开发中,引用的概念是同样适用的,在 Python 中使用类创建对象之后tom 变量中 仍然记录的是对象在内存中的地址
  • 也就是 tom 变量引用了新建的猫对象
  • 使用 print 输出对象变量,默认情况下,是能够输出这个变量引用的对象由哪一个类创建的对象,以及
    在内存中的地址(十六进制表示)
  • 提示:在计算机中,通常使用十六进制表示内存地址
    • 十进制十六进制都是用来表达数字的,只是表示的方式不一样
    • 十进制十六进制的数字之间可以来回转换
  • %d 可以以10 进制输出数字
  • %x 可以以16 进制输出数字
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.2 方法中的self参数

3.2.1 给对象增加属性

Python 中,要给对象设置属性,只需要在类的外部的代码中直接通过 . 设置一个属性即可。但是不推荐使用,因为对象属性的封装应该封装在类的内部

tom.name = "Tom"
...
lazy_cat.name = "大懒猫"

提示

  • 在日常开发中,不推荐在类的外部给对象增加属性
    • 如果在运行时,没有找到属性,程序会报错
  • 对象应该包含有哪些属性,应该封装在类的内部
    在这里插入图片描述

3.2.2 self的意义和用法

  • 哪一个对象调用的方法,方法内的 self 就是哪一个对象的引用
  • 在类封装的方法内部,self 就表示当前调用方法的对象自己
  • 调用方法时,程序员不需要传递 self 参数
  • 在方法内部
    • 可以通过 self. 访问对象的属性
    • 也可以通过 self. 调用其他的对象方法
      在这里插入图片描述
  • 类的外部,通过变量名.访问对象的属性和方法
  • 类封装的方法中,通过 self.访问对象的属性和方法

3.3 dir内置函数

  • Python对象几乎是无所不在的,我们之前学习的变量、数据、函数都是对象
  • Python 中可以使用以下两个方法验证:
    • 标识符 / 数据后输入一个 .,然后按下 TAB 键,iPython 会提示该对象能够调用的方法列表
    • 使用内置函数 dir 传入标识符 / 数据,可以查看对象内的所有属性及方法
  • 提示 __方法名__ 格式的方法是 Python 提供的内置方法 / 属性,稍后会给大家介绍一些常用的内置方法 / 属性
序号方法名类型作用
01__new__方法创建对象时,会被自动调用
02__init__方法对象被初始化时,会被自动调用
03__del__方法对象被从内存中销毁前,会被自动调用
04__str__方法返回对象的描述信息,print 函数输出使用
  • 提示"利用好 dir() 函数,在学习时很多内容就不需要死记硬背了
    在这里插入图片描述
    在这里插入图片描述

3.3.1 __init__方法

  • 当使用类名() 创建对象时,会自动执行以下操作:

    • 为对象在内存中分配空间 —— 创建对象
    • 为对象的属性设置初始值 —— 初始化方法(init)
  • 这个初始化方法就是 __init__ 方法,__init__两个下划线)是对象的内置方法

  • __init__ 方法是专门用来定义一个类具有哪些属性的方法!

  • 验证该方法在创建对象时会被自动调用
    在这里插入图片描述

在初始化方法内部定义属性

  • __init__ 方法内部使用 self.属性名 = 属性的初始值 就可以定义属性
  • 定义属性之后,再使用 Cat创建的对象都会拥有该属性
    在这里插入图片描述
    在这里插入图片描述

初始化的同时设置初始值

  • 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行改造
  • 把希望设置的属性值,定义成 __init__ 方法的参数
  • 在方法内部使用 self.属性 = 形参 接收外部传递的参数
  • 创建对象时,使用 类名(属性1, 属性2...) 调用
    在这里插入图片描述

3.3.2 __del__方法

  • Python
    • 当使用 类名() 创建对象时,为对象分配完空间后,自动调用 __init__ 方法
    • 当一个对象被从内存中销毁前,会自动调用 __del__ 方法
      在这里插入图片描述
      在这里插入图片描述
  • 应用场景
    • __init__ 改造初始化方法,可以让创建对象更加灵活
    • __del__ 如果希望在对象被销毁前,再做一些事情,可以考虑一下 __del__ 方法
  • 生命周期
    • 一个对象从调用类名() 创建,生命周期开始
    • 一个对象的 __del__ 方法一旦被调用,生命周期结束
    • 在对象的生命周期内,可以访问对象属性,或者让对象调用方法

3.3.3 __str__方法

  • Python 中,使用 print 输出对象变量,默认情况下,会输出这个变量引用的对象由哪一个类创建的对象,以及在内存中的地址(十六进制表示)
  • 如果在开发中,希望使用 print 输出对象变量时,能够打印自定义的内容,就可以利用 __str__ 这个内置方法了
  • 注意:__str__ 方法必须返回一个字符串
    在这里插入图片描述

04 面向对象的封装

  • 封装是面向对象编程的一大特点
  • 面向对象编程
  1. 属性和方法封装到一个抽象的
  2. 外界使用创建对象,然后让对象调用方法
  3. 对象方法的细节都被封装在类的内部
    在这里插入图片描述

提示:

  • 对象的方法内部,是可以直接访问对象的属性的
  • 同一个类创建的多个对象之间,属性互不干扰!
  • 被使用的类,通常应该先开发
  • 一个对象的属性可以是另外一个类创建的对象
    在这里插入图片描述

4.1 定义没有初始值的属性None

在定义属性时,如果不知道设置什么初始值,可以设置为None

  • None关键字表示什么都没有
  • 表示一个空对象,没有方法和属性,是一个特殊的常量
  • 可以将None赋值给任何一个变量

4.2 身份运算符is

身份运算符用于比较两个对象的内存地址是否一致 —— 是否是对同一个对象的引用

  • 在 Python 中针对 None 比较时,建议使用 is 判断
运算符描述实例
isis 是判断两个标识符是不是引用同一个对象x is y,类似 id(x) == id(y)
is notis not 是判断两个标识符是不是引用不同对象x is not y,类似 id(a) != id(b)

is== 区别:

  • is 用于判断两个变量引用对象是否为同一个
  • == 用于判断引用变量的值是否相等

在这里插入图片描述

4.3 私有属性和私有方法

在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到

  • 私有属性就是对象不希望公开的属性
  • 私有方法就是对象不希望公开的方法

4.3.1 私有属性和方法的定义方式

在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
在这里插入图片描述
注:Python中其实是伪私有属性和私有方法,没有真正意义上的私有

  • 在给属性、方法命名时,实际是对名称做了一些特殊处理,使得外界无法访问到
  • 处理方式:在名称前面加上 _类名 => _类名__名称
    在这里插入图片描述

05 继承

5.1 单继承

5.1.1 继承的概念、语法和特点

① 继承的概念:子类拥有父类的所有方法和属性
② 继承的语法

class 类名(父类名):
   
       pass
  • 子类继承自父类,可以直接使用父类中已经封装好的方法,不需要再次开发
  • 子类中应该根据功能,封装子类特有的属性和方法

③专业术语

  • Dog 类是 Animal 类的子类Animal 类是 Dog 类的父类Dog 类从 Animal继承
  • Dog 类是 Animal 类的派生类Animal 类是 Dog 类的基类Dog 类从 Animal派生
    ④继承的传递性
  • C类B类继承,B类又从A类继承那么C类就具有B类A类的所有属性和方法
  • 子类拥有父类以及父类的父类中封装的所有属性和方法

方法的重写

当父类的方法实现不能满足子类需求时,可以对方法进行重写(override)
在这里插入图片描述
重写父类方法有两种情况:

  • 覆盖父类的方法
  • 对父类方法进行扩展

①覆盖父类的方法

  • 如果在开发中,父类的方法实现和子类的方法实现,完全不同就可以使用覆盖的方式,在子类中重新编写父类的方法实现
  • 具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现
  • 重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法
    在这里插入图片描述

② 对父类方法进行扩展

  • 如果在开发中,子类的方法实现中包含父类的方法实现父类原本封装的方法实现 是 子类方法的一部分就可以使用扩展的方式在子类中重写父类的方法
  • 在需要的位置使用 super().父类方法来调用父类方法的执行
  • 代码其他的位置针对子类的需求,编写子类特有的代码实现
    在这里插入图片描述

注:关于 super()

  • Pythonsuper 是一个特殊的类
  • super() 就是使用 super 类创建出来的对象
  • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
    提示
  • 在开发时,父类名.方法(self)super().方法 两种方式不要混用
  • 如果使用当前子类名调用方法,会形成递归调用,出现死循环

5.1.2 父类的私有属性和私有方法

  • 子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法
  • 子类对象可以通过父类的公有方法 间接访问到私有属性或私有方法
  • 私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直接访问
  • 私有属性、方法通常用于做一些内部的事情

在这里插入图片描述

  • B的对象不能直接访问 __num2 属性
  • B的对象不能在 demo 方法内访问 __num2 属性
  • B的对象可以在 demo方法内,调用父类的 test 方法
  • 父类的 test 方法内部,能够访问 __num2 属性和 __test 方法

在这里插入图片描述在这里插入图片描述

5.2 多继承

5.2.1 概念

  • 子类可以拥有多个父类,并且具有所有父类属性和方法
  • 语法
class 子类名(父类名1, 父类名2...)
    pass

在这里插入图片描述
在这里插入图片描述

5.2.2 多继承的使用注意事项

  • 开发时,应该尽量避免不同的父类中存在同名的方法这种容易产生混淆的情况! —— 如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承
    在这里插入图片描述
    在这里插入图片描述
  • Python 中的 MRO —— 方法搜索顺序(了解)
    • Python 中针对提供了一个内置属性__mro__ 可以查看方法搜索顺序
    • MRO是 method resolution order,主要用于在多继承时判断方法、属性的调用路径
      在这里插入图片描述
    • 在搜索方法时,是按照 __mro__ 的输出结果从左至右的顺序查找的
    • 如果在当前类中找到方法,就直接执行,不再搜索
    • 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
    • 如果找到最后一个类,还没有找到方法,程序报错

5.2.3 新式类与旧式(经典)类

objectPython 为所有对象提供的基类,提供有一些内置的属性和方法,可以使用 dir 函数查看

  • 新式类:以 object 为基类的类,推荐使用
  • 经典类:不以 object 为基类的类,不推荐使用
  • Python 3.x 中定义类时,如果没有指定父类,会默认使用 object 作为该类的基类 —— Python 3.x中定义的类都是新式类
  • Python 2.x 中定义类时,如果没有指定父类,则不会object 作为基类
  • 新式类经典类在多继承时 —— 会影响到方法的搜索顺序
  • 为了保证编写的代码能够同时在 Python 2.xPython 3.x 运行,今后在定义类时,如果没有父类,建议统一继承自object
class 类名(object):
    pass

06 多态

面向对象三大特性

  • 封装根据职责将属性和方法封装到一个抽象的类中
    • 定义类的准则
  • 继承实现代码的重用,相同的代码不需要重复的编写
    • 设计类的技巧
    • 子类针对自己特有的需求,编写特定的代码
  • 多态不同的子类对象调用相同的父类方法,产生不同的执行结果
    • 多态可以增加代码的灵活度
    • 继承重写父类方法为前提
    • 是调用方法的技巧,不会影响到类的内部设计
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

07 类属性和类方法

7.1 类的结构

7.1.1 术语 —— 实例

1.使用面向对象开发,第 1 步是设计
2.使用类名() 创建对象,创建对象的动作有两步:

  • ①在内存中为对象分配空间
  • ②调用初始化方法 __init__ 为 对象初始化

3.对象创建后,内存中就有了一个对象的实实在在的存在 —— 实例
在这里插入图片描述
因此,通常也会把:

  • 创建出来的对象叫做类的实例
  • 创建对象的动作叫做实例化
  • 对象的属性叫做实例属性
  • 对象调用的方法叫做实例方法

在程序执行时:

  1. 对象各自拥有自己的实例属性
  2. 调用对象方法,可以通过 self.
    -访问自己的属性
    -调用自己的方法

结论

  • 每一个对象都有自己独立的内存空间保存各自不同的属性
  • 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

7.1.2 类是一个特殊的对象

  • Python一切皆对象

    • class AAA: 定义的类属于类对象
    • obj1 = AAA() 属于实例对象
  • 在程序运行时,同样会被加载到内存

  • Python 中,是一个特殊的对象 —— 类对象

  • 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例

  • 除了封装实例属性和方法外,类对象还可以拥有自己的属性和方法

    • 类属性
    • 类方法
  • 通过类名. 的方式可以访问类的属性或者调用类的方法

  • 对象的实例方法保存在对象的内存空间中,当一个对象调用实例方法时,对象会将引用当作参数传递给方法
    在这里插入图片描述

7.2 类属性和实例属性

7.2.1 概念和使用

  • 类属性就是给类对象中定义的属性
  • 通常用来记录与这个类相关的特征
  • 类属性不会用于记录具体对象的特征
    在这里插入图片描述

7.2.2 属性的获取机制

  • python属性的获取存在一个向上查找机制,即若在对象中没找到该属性,会去其查找创建该对象的类中有无这个属性。
    在这里插入图片描述
    在这里插入图片描述

  • 因此,要访问类属性有两种方式:

    • 类名.类属性
    • 对象.类属性 (不推荐,阅读代码可能混淆)

注意

  • 如果使用对象.类属性 = 值赋值语句,只会给对象添加一个属性,而不会影响到类属性的值
    在这里插入图片描述

7.3 类方法和静态方法

7.3.1 类方法

  • 类属性就是针对类对象定义的属性
    • 使用赋值语句class 关键字下方可以定义类属性
    • 类属性用于记录与这个类相关的特征
  • 类方法就是针对类对象定义的方法
    • 类方法内部可以直接访问类属性或者调用其他的类方法

语法如下

@classmethod
def 类方法名(cls):
    pass
  • 类方法需要用修饰器 @classmethod 来标识,告诉解释器这是一个类方法
  • 类方法的第一个参数应该是 cls
    • 哪一个类调用的方法,方法内的 cls 就是哪一个类的引用
    • 这个参数和实例方法的第一个参数是 self 类似
    • 提示使用其他名称也可以,不过习惯使用 cls
  • 通过类名. 调用类方法时,不需要传递 cls 参数
  • 在方法内部
    • 可以通过 cls. 访问类的属性
    • 也可以通过 cls. 调用其他的类方法

在这里插入图片描述

7.3.2 静态方法

  • 在开发时,如果需要在中封装一个方法,这个方法:
    • 不需要访问实例属性或者调用实例方法
    • 不需要访问类属性或者调用类方法
  • 这时,可以把这个方法封装成一个静态方法

语法如下

@staticmethod
def 静态方法名():
    pass
  • 静态方法需要用修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
  • 通过类名. 调用静态方法

在这里插入图片描述
案例小结

  • 实例方法 —— 方法内部需要访问实例属性
    • 实例方法内部可以使用类名. 访问类属性
  • 类方法 —— 方法内部只需要访问类属性
  • 静态方法 —— 方法内部,不需要访问实例属性类属性
  • 如果方法内部,既需要访问实例属性,又需要访问类属性,应定义成实例方法
    在这里插入图片描述
    在这里插入图片描述

08 单例

8.1 单例设计模式

  • 设计模式
  • 设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
  • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
  • 单例设计模式
  • 目的—— 让创建的对象,在系统中只有唯一的一个实例
  • 每一次执行类名()返回的对象,内存地址是相同的

8.1.1 __new__方法

  • 使用类名()创建对象时,Python的解释器首先会调用 __new__ 方法为对象分配空间
  • __new__ 是一个由object基类提供的内置的静态方法,主要作用有两个:
    • ①在内存中为对象分配空间
    • 返回对象的引用
  • Python 的解释器获得对象的引用后,将引用作为第一个参数,传递给 __init__ 方法
  • 重写 __new__ 方法一定要 return super().__new__(cls)
  • 否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
  • 注意:__new__ 是一个静态方法,在调用时需要主动传递cls参数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2 Python中的单例

  • 单例 —— 让类创建的对象,在系统中只有唯一的一个实例
    • 定义一个类属性,初始值是 None,用于记录单例对象的引用
    • 重写__new__ 方法
    • 如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
    • 返回类属性中记录的对象引用
      在这里插入图片描述
  • __new__ 方法改造,使得每次都会得到第一次被创建对象的引用
    在这里插入图片描述
  • 对初始化方法__init__进行改造,使得只执行一次初始化操作
  • 解决办法
    • 定义一个类属性init_flag标记是否执行过初始化动作,初始值为False
    • __init__ 方法中,判断 init_flag,如果为False就执行初始化动作
    • 然后将 init_flag设置为True
    • 这样,再次自动调用 __init__ 方法时,初始化动作就不会被再次执行了
      在这里插入图片描述

09 异常

9.1 异常的概念

  • 程序在运行时,如果Python解释器遇到到一个错误会停止程序的执行,并且提示一些错误信息,这就是异常
  • 程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出(raise)异常
    在这里插入图片描述
    程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性

9.2 捕获异常

9.2.1 简单的捕获异常语法

  • 在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加 try(尝试)捕获异常
  • 捕获异常最简单的语法格式:
try:
    尝试执行的代码
except:
    出现错误的处理
  • try 尝试,下方编写要尝试代码,不确定是否能够正常执行的代码
  • except 如果不是,下方编写尝试失败的代码

在这里插入图片描述

9.3 错误类型捕获

  • 在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
  • 语法如下:
try:
    # 尝试执行的代码
    pass
except 错误类型1:
    # 针对错误类型1,对应的代码处理
    pass
except (错误类型2, 错误类型3):
    # 针对错误类型2 和 3,对应的代码处理
    pass
except Exception as result:
    print("未知错误 %s" % result)
  • Python 解释器抛出异常时,最后一行错误信息的第一个单词,就是错误类型
    在这里插入图片描述
    捕获未知错误
  • 在开发时,要预判到所有可能出现的错误,还是有一定难度的
  • 如果希望程序无论出现任何错误,都不会因为 Python 解释器抛出异常而被终止,可以再增加一个except

语法如下:

except Exception as result:
    print("未知错误 %s" % result)

在这里插入图片描述

9.3.1 异常捕获完整语法

  • 在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下:
try:
    # 尝试执行的代码
    pass
except 错误类型1:
    # 针对错误类型1,对应的代码处理
    pass
except 错误类型2:
    # 针对错误类型2,对应的代码处理
    pass
except (错误类型3, 错误类型4):
    # 针对错误类型3 和 4,对应的代码处理
    pass
except Exception as result:
    # 打印错误信息
    print(result)
else:
    # 没有异常才会执行的代码
    pass
finally:
    # 无论是否有异常,都会执行的代码
    print("无论是否有异常,都会执行的代码")
  • else只有在没有异常时才会执行的代码
  • finally无论是否有异常,都会执行的代码
    在这里插入图片描述

9.4 异常的传递

  • 异常的传递——当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方
  • 如果传递到主程序,仍然没有异常处理,程序才会被终止
    在这里插入图片描述
    提示
  • 在开发中,可以在主函数中增加异常捕获
  • 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中
  • 这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁
    在这里插入图片描述

9.5 抛出raise异常

9.5.1 应用场景

  • 在开发中,除了代码执行出错 Python 解释器会抛出异常之外
  • 还可以根据应用程序特有的业务需求主动抛出异常
  • 示例:提示用户输入密码,如果长度少于 8,抛出异常
    • 当前函数只负责提示用户输入密码,如果密码长度不正确,需要其他的函数进行额外处理
    • 因此可以抛出异常,由其他需要处理的函数捕获异常
      在这里插入图片描述

9.5.2 抛出异常

  • Python 中提供了一个Exception异常类
  • 在开发时,如果满足特定业务需求时,希望抛出异常,可以:
  • 创建 一个Exception对象
  • 使用raise关键字抛出异常对象
    在这里插入图片描述

10 模块和包

10.1 模块

10.1.1 模块的概念

  • 模块是Python程序架构的一个核心概念
  • 每一个以扩展名 py 结尾的 Python 源代码文件都是一个模块
  • 模块名同样也是一个标识符,需要符合标识符的命名规则
  • 在模块中定义的全局变量 、函数、类都是提供给外界直接使用的工具
  • 模块就好比是工具包,要想使用这个工具包中的工具,就需要先导入这个模块

10.1.2 模块导入的方式

10.1.2.1 import导入
  • 提示:在导入模块时,每个导入应该独占一行
import 模块名1
import 模块名2 
  • 导入之后
    • 通过模块名. 使用模块提供的工具 —— 全局变量、函数、类
    • 使用as指定模块的别名,如果模块的名字太长,可以使用 as 指定模块的名称,以方便在代码中的使用
import 模块名1 as 模块别名

注意:模块别名应该符合大驼峰命名法

10.1.2.2 from…import导入
  • 如果希望从某一个模块中,导入部分工具,就可以使用 from ... import 的方式
  • import 模块名一次性把模块中所有工具全部导入,并且通过模块名/别名访问
# 从模块导入某一个工具
from 模块名1 import 工具名
  • 导入之后
    • 不需要通过模块名.
    • 可以直接使用模块提供的工具 —— 全局变量、函数、类

注意;如果两个模块,存在同名的函数,那么后导入模块的函数,会覆盖掉先导入的函数

  • 开发时 import 代码应该统一写在代码的顶部,更容易及时发现冲突
  • 一旦发现冲突,可以使用 as 关键字给其中一个工具起一个别名
  • 导入模块中所有工具from...import *,在使用时不需要使用模块名.的方式
# 从模块导入所有工具
from 模块名1 import *

10.1.3 模块的搜索顺序

  • Python的解释器在导入模块时,会:
    • 搜索当前目录指定模块名的文件,如果有就直接导入
    • 如果没有,再搜索系统目录
  • 在开发时,给文件起名,不要和系统的模块文件重名
  • Python 中每一个模块都有一个内置属性 __file__ 可以查看模块完整路径
    在这里插入图片描述
    注意:如果当前目录下,存在一个 random.py 的文件,程序就无法正常执行了!这时,Python 的解释器会加载当前目录下的 random.py 而不会加载系统的 random 模块

10.1.4 原则 – 每一个文件都应该是可以被导入的

  • 一个独立的Python文件就是一个模块
  • 在导入文件时,文件中所有没有任何缩进的代码都会被执行一遍
10.1.4.1 __name__方法
  • __name__属性可以做到,测试模块的代码只在测试情况下被运行,而在被导入时不会被执行
  • __name__Python 的一个内置属性,记录着一个字符串
    • 如果是被其他文件导入的,__name__ 就是模块名
    • 如果是当前执行的程序 __name____main__
      在这里插入图片描述
      在这里插入图片描述

在很多Python文件中都会看到以下格式的代码:

# 导入模块
# 定义全局变量
# 定义类
# 定义函数

# 在代码的最下方
def main():
    # ...
    pass

# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
    main()

10.2 包(Package)

10.2.1 概念

  • 是一个包含多个模块特殊目录
  • 目录下有一个特殊的文件 __init__.py
  • 包名的命名方式和变量名一致,小写字母 + _,不能以数字开头
  • 使用 import 包名 可以一次性导入包中所有的模块

10.2.2 建立包的两种方式

  • 右键点击项目文件—New—Directory—新建目录—右键点击目录—New—Python File—新建名为__init__的py文件
  • 右键点击项目文件—New—Python Package—新建包
    在这里插入图片描述
  • 要在外界使用中的模块,需要在 __init__.py 中指定对外界提供的模块列表
# 从 当前目录导入模块列表
from . import send_message
from . import receive_message

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10.3 发布模块

10.3.1 制作发布压缩包步骤

① 创建setuo.py

  • setup.py 的文件,其保存在包的PKG-INFO文件中
from distutils.core import setup

setup(name="hm_message",  # 包名
      version="1.0",  # 版本
      description="itheima's 发送和接收消息模块",  # 描述信息
      long_description="完整的发送和接收消息模块",  # 完整描述信息
      author="itheima",  # 作者
      author_email="itheima@itheima.com",  # 作者邮箱
      url="www.itheima.com",  # 主页
      py_modules=["hm_message.send_message",
                  "hm_message.receive_message"])
$ python3 setup.py build

在这里插入图片描述

③ 生成发布压缩包

  • 添加参数sdist
$ python3 setup.py sdist

在这里插入图片描述

注意:要制作哪个版本的模块,就使用哪个版本的解释器执行!

10.3.2 安装模块

$ tar -zxvf message-1.0.tar.gz 

$ sudo python3 setup.py install

10.3.3 卸载模块

直接从安装目录下,把安装模块的目录删除就可以

$ cd /usr/local/lib/python3.7/dist-packages/
$ sudo rm -r message*

10.3.4 pip3安装第三方模块

  • 第三方模块通常是指由知名的第三方团队开发的并且被程序员广泛使用Python包 / 模块
  • 例如 pygame 就是一套非常成熟的游戏开发模块
  • pip3 是一个现代的,通用的 Python 包管理工具
  • 提供了对Python包的查找、下载、安装、卸载等功能

安装和卸载命令如下:

# 将模块安装到 Python 2.x 环境
$ sudo pip install pygame
$ sudo pip uninstall pygame


# 将模块安装到 Python 3.x 环境
$ sudo pip3 install pygame
$ sudo pip3 uninstall pygame

  • Mac 下安装 iPython
$ sudo pip install ipython
  • Linux 下安装 iPython
$ sudo apt install ipython3

10.3.5 __file__属性

  • 可以查看模块或包源文件的完整路径
    在这里插入图片描述

11 文件

11.1 文件的概念

11.1.1 文件的概念和作用

  • 计算机的文件,就是存储在某种长期储存设备上的一段数据
  • 长期存储设备包括:硬盘、U 盘、移动硬盘、光盘…

文件的作用:将数据长期保存下来,在需要的时候使用

11.1.2 文件的存储方式

  • 在计算机中,文件是以二进制的方式保存在磁盘上的

文本文件和二进制文件

  • 文本文件
    • 可以使用文本编辑软件查看
    • 本质上还是二进制文件
    • 例如:python 的源程序
  • 二进制文件
    • 保存的内容不是给人直接阅读的,而是提供给其他软件使用的
    • 例如:图片文件、音频文件、视频文件等等
    • 二进制文件不能使用文本编辑软件查看

11.2 文件的基本操作

11.2.1 操作文件的套路

计算机中要操作文件的套路非常固定,一共包含三个步骤

  • ① 打开文件
  • ② 读、写文件
    • 将文件内容读入内存
    • 将内存内容写入文件
  • ③ 关闭文件

11.2.2 操作文件的函数/方法

  • Python中要操作文件需要记住 1 个函数和 3 个方法
序号函数/方法说明
01open打开文件,并且返回文件操作对象
02read将文件内容读取到内存
03write将指定内容写入文件
04close关闭文件
  • open 函数负责打开文件,并且返回文件对象
  • read/write/close 三个方法都需要通过文件对象来调用

11.2.3 read方法——读取文件

  • open函数的第一个参数是要打开的文件名(文件名区分大小写
    • 如果文件存在,返回文件操作对象
    • 如果文件不存在,会抛出异常
  • read 方法可以一次性读入返回文件的所有内容
  • close方法负责关闭文件
    • 如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
  • 注意:read方法执行后,会把文件指针移动到文件的末尾
  • 提示:在开发中,通常会先编写打开关闭的代码,再编写中间针对文件的读/写操作!
    在这里插入图片描述

11.2.4 文件指针

  • 文件指针标记从哪个位置开始读取数据
  • 第一次打开文件时,通常文件指针会指向文件的开始位置
  • 当执行了read方法后,文件指针会移动到读取内容的末尾
    • 默认情况下会移动到文件末尾
  • 如果执行了一次read方法,读取了所有内容,文件指针移动到了文件末尾,再次调用read 方法不会读取到任何的内容
    在这里插入图片描述

11.2.5 打开文件的方式

  • open函数默认以只读方式打开文件,并且返回文件对象
    在这里插入图片描述

语法如下:

f = open("文件名", "访问方式")
访问方式说明
r只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常
w只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件
a追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入
r+读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常
w+读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件
a+读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入
  • 提示:频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以只读、只写的方式来操作文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

11.3 按行读取文件内容

  • read 方法默认会把文件的所有内容一次性读取到内存
  • 如果文件太大,对内存的占用会非常严重

readline 方法

  • readline 方法可以一次读取一行内容
  • 方法执行后,会把文件指针移动到下一行,准备再次读取
    在这里插入图片描述

复制小文件
在这里插入图片描述
复制大文件
在这里插入图片描述

11.4 文件/目录的常用管理操作

  • 终端 / 文件浏览器中可以执行常规的文件 / 目录管理操作,例如:创建、重命名、删除、改变路径、查看目录内容
  • Python 中,如果希望通过程序实现上述功能,需要导入 os 模块

11.4.1 文件操作

序号方法名说明示例
01rename重命名文件os.rename(源文件名, 目标文件名)
02remove删除文件os.remove(文件名)

11.4.2 目录操作

序号方法名说明示例
01listdir目录列表os.listdir(目录名)
02mkdir创建目录os.mkdir(目录名)
03rmdir删除目录os.rmdir(目录名)
04getcwd获取当前目录os.getcwd()
05chdir修改工作目录os.chdir(目标目录)
06path.isdir判断是否是文件os.path.isdir(文件路径)

提示:文件或者目录操作都支持相对路径绝对路径

11.5 文本文件的编码格式

  • 文本文件存储的内容是基于字符编码的文件,常见的编码有 ASCII 编码,UNICODE 编码等
  • Python 2.x 默认使用 ASCII 编码格式
  • Python 3.x 默认使用 UTF-8 编码格式

11.5.1 ASCII编码和UNICODE编码

ASCII编码

  • 计算机中只有256ASCII 字符
  • 一个 ASCII 在内存中占用1 个字节的空间
    • 80/1 的排列组合方式一共有 256 种,也就是 2 ** 8
    • Python 2.x 默认使用 ASCII 编码格式,因此不能直接使用中文

在这里插入图片描述
UTF-8 编码格式

  • 计算机中使用1~6个字节 来表示一个UTF-8字符,涵盖了地球上几乎所有地区的文字
  • 大多数汉字会使用3 个字节表示
  • UTF-8UNICODE 编码的一种编码格式
  • Python 3.x 默认使用 UTF-8 编码格式,因此可以直接使用中文

11.5.2 Ptyhon 2.x 中如何使用中文

  • Python 2.x 文件的第一行增加以下代码,解释器会以 UTF-8 编码来处理 Python 文件
# *-* coding:utf8 *-*
  • 也可以使用
# coding=utf8

11.5.3 Unicode字符串

  • Python 2.x 中,即使指定了文件使用 UTF-8 的编码格式,但是在遍历字符串时,仍然会以字节为单位遍历字符串
  • 要能够正确的遍历字符串,在定义字符串时,需要在字符串的引号前,增加一个小写字母 u,告诉解释器这是一个 unicode 字符串(使用UTF-8 编码格式的字符串)
    在这里插入图片描述
    在这里插入图片描述

12 eval 函数

  • eval() 函数十分强大 —— 将字符串当成有效的表达式来求值并返回计算结果
    在这里插入图片描述
  • 不要滥用 eval,在开发时千万不要使用 eval 直接转换 input 的结果。因为用户可以直接通过eval调用终端命令进行各种操作。执行成功返回0,执行失败返回错误信息。
    在这里插入图片描述

12.1 __import__方法

__import__('os').system('ls')

等价代码

import os

os.system("终端命令")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值