01 面向对象基础语法
dir内置函数
在Python中可以使用以下两个方法验证:
- 在标识符/数据后输入一个
.,然后按下TAB键,iPython会提示该对象能够调用的方法列表 - 使用内置函数
dir传入标识符/数据,可以查看对象内的所有属性及方法
提示__方法名__格式的方法是Python提供的内置方法/属性,dir()函数可查看
| 方法名 | 类型 | 作用 |
|---|---|---|
__new__ |
方法 | 创建对象时,会被自动调用 |
__init__ |
方法 | 对象被初始化时,会被自动调用 |
__del__ |
方法 | 对象被从内存中销毁前,会被自动调用 |
__str__ |
方法 | 返回对象的描述信息,print函数输出使用 |
02 定义简单的类
1.定义只包含方法的类
class 类名:def 方法1(self, 参数列表):passdef 方法2(self, 参数列表):pass
- 方法的定义格式和之前学习过的函数几乎一样
- 区别在于第一个参数必须是
self - 类名的命名规则要符合大驼峰命名法
2.创建对象
- 格式:
对象变量 = 类名()
%d可以以10进制输出数字
%x可以以16进制输出数字
03 方法中的self函数
1.给对象增加属性
- 在
Python中,要给对象设置属性,只需要在类的外部代码中直接通过.设置一个属性即可(不推荐使用)
2.使用self方法
- 由哪一个对象调用的方法,方法内的
self就是哪一个对象的引用 - 在类封装的方法内部,
self就表示当前调用方法的对象自己 - 调用方法时,程序员不需要传递
self参数 - 在方法内部
- 可以通过
self.访问对象的属性 - 也可以通过
self.调用其他的对象方法
- 可以通过
04 初始化方法
1.初始化方法init
- 在使用
类名()创建对象时,会自动执行以下操作:- 为对象在内存中分配空间——创建对象
- 为对象的属性设置初始值——初始化方法(
init)
- 这个初始化方法就是
__init__方法,__init__是对象的内置方法
__init__方法时专门用来定义一个类具有哪些属性的方法
2.在初始化方法内部定义属性
- 在
__init__方法内部使用self.属性名 = 属性的初始值就可以定义属性 - 定义属性之后,再使用
Cat类创建的对象,都会拥有该属性
3.初始化同时设置初始值
- 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对
__init__方法进行改造- 把希望设置的属性值,定义成
__init__方法的参数 - 在方法内部使用
self.属性 = 形参接受外部传递的参数 - 在创建对象时,使用
类名(属性1, 属性2……)调用
- 把希望设置的属性值,定义成
05 内置方法和属性
| 方法名 | 类型 | 作用 |
|---|---|---|
__del__ |
方法 | 对象被从内存中销毁前,会被自动调用 |
__str__ |
方法 | 返回对象的描述信息,print函数输出使用 |
5.1 __del__方法
__init__与__del__类似于C++中的构造函数和析构函数
del关键字可以删除一个对象
5.2 __str__方法
- 在
Python中,使用print输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示) - 如果在开发中,希望使用
print输出对象变量是,能够打印自定义的内容,就可以利用__str__这个内置方法了
__str__方法必须返回一个字符串
06 封装案例练习
1.定义没有初始值的属性
在定义属性时,如果不知道设置什么初始值,可以设置为None
None关键字表示什么都没有- 表示一个空对象,没有方法和属性,是一个特殊的常量
- 可以将
None赋值给任何一个变量
2.身份运算符
身份运算符用于比较两个对象的内存地址是否一致——是否是对同一个对象的引用
- 在
Python中针对None比较时,建议使用is判断 | 运算符 | 描述 | 实例 | | —- | —- | —- | | is | is 是判断两个标识符是不是引用同一个对象 | x is y,类似id(x) == id(y) | | is not | is not 是判断两个标识符是不是引用不同对象 | x is not y,类似id(x) != id(y) |
is与==区别:
is用于判断两个变量 引用对象是否为同一个
==用于判断引用变量的值是否相等
07 私有属性和私有方法
1.定义方式
- 在定义属性和方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
2.为私有属性和私有方法
在日常开发中,不要使用这种方式,访问对象的私有属性或私有方法
Python中,并没有真正意义的私有
- 在给属性、方法命名时,实际上对名称做了一些特殊处理,使得外界无法访问到
- 处理方式:在名称前面加上
_类名=>_类名__名称
08 继承
1.单继承
1.1 继承的语法
class 类名(父类名):pass
Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类继承Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生- 子类拥有父类以及父类的父类中封装的所有属性和方法
1.2 方法的重写
1.2.1 覆盖子类的方法
- 在子类中定义一个和父类同名的方法并且实现
1.2.2 对父类方法进行扩展
- 在子类中 重写 父类的方法
- 在需要的位置使用
super().父类方法来调用父类方法的执行 - 代码其他的位置针对子类的需求,编写子类特有的代码实现
关于super
- 在
Python中super是一个特殊的类 super()就是使用super类创建出来的对象- 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
class XiaoTianQuan(Dog):def bark(self):print("bark like god~~")super().bark()# Dog.bark(self) 不推荐使用
1.2.3 调用父类方法的另外一种形式
父类名.方法(self)
1.3 父类的 私有属性 和 私有方法
- 子类对象 不能 在自己的方法内部,直接访问父类的私有属性或私有方法
- 子类对象可以通过父类的公有方法 间接访问到私有属性或私有方法
- 私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能访问
- 私有属性、方法通常用于做一些内部的事情
2.多继承
2.1 多继承的语法
- 子类可以拥有多个父类,并且具有所有父类的属性和方法
class 子类名(父类名1, 父类名2...):pass
2.2 Python中的MRO —— 方法搜索顺序
Python中针对类提供了一个内置属性__mro__可以查看方法搜索顺序- MRO是
method resolution order,主要用于在多继承时判断 方法、属性 的调用 路径print(C.__mro__) - 在搜索方法时,是按照
__mro__的输出结果从左至右的顺序查找的 - 如果在当前类中找到方法,就直接执行,不在搜索
- 如果没有找到,就查找下一个类中是否有对性的方法,如果找到,就直接执行,不在搜索
- 如果找到最后一个类,还没有找到方法,程序报错
2.3 新式类与旧式(经典)类
object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
- 新式类:以
object为基类的类 - 旧式类:不以
object为基类的类 - 在
Python 3.x中定义类时,如果没有指定父类,会默认使用object作为该类的基类——Python 3.x中定义的类都是新式类
为了保证编写的代码能够同时在Python 2.x和Python 3.x运行!
今后在定义类时,如果没有父类,建议统一继承自object
class 类名(object):pass
09 多态
不同的子类对象调用相同的父类方法,产生不同的执行结果
- 多态可以增加代码的灵活度
- 以继承和重写父类方法为前提
- 时调用方法的技巧,不会影响到类的内部设计
10 类的结构
1.实例
- 使用面向对象开发,第1步时设计类
- 使用类名()创建对象,**创建对象的动作有两步:
- 在内存中为对象分配空间
- 调用初始化方法
__init__为对象初始化
- 对象创建后,内存中就有了一个对象的实实在在的存在——实例
- 每一个对象都有自己独立的内存空间,保存各自不同的属性
- 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
2.类是一个特殊的对象
Python中一切及对象:
class AAA:定义的类属于类对象obj1 = AAA()属于实例对象
- 通过类名.的方式可以访问类的属性或者调用类的方法
3.类属性和实例属性
3.1 类属性的定义
- 类属性就是给类对象中定义的属性
- 通常用来记录与这个类相关的特征
- 类属性 不会用于记录具体对象的特征
class Tool(object):# 类属性count = 0# 实例属性def __init__(self, name):self.name = nameTool.count += 1
3.2 属性的获取机制
- 在
Python中属性的获取存在一个向上查找的机制
# 首先在对象内部查找对象属性,查找不到向上查找类属性tool1.count
4.类方法和静态方法
4.1 类方法
- 类方法就是针对类对象定义的方法
- 在类方法内部可以直接访问类属性或者调用其他的类方法
@classmethoddef 类方法名(cls):pass
- 类方法需要用修饰器
@classmethod来标识,告诉解释器这是一个类方法 - 类方法的第一个参数应该是
cls- 由哪一个类调用的方法,方法内的
cls就是哪一个类的引用 - 这个参数和实例方法的第一个参数是
self类似 - 提示 使用其他名称也可以,不过习惯使用
cls
- 由哪一个类调用的方法,方法内的
- 通过类名,调用类方法,调用方法时,不需要传递
cls参数 - 在方法内部
- 可以通过
cls.访问类的属性 - 也可以通过
cls.调用其他类方法
- 可以通过
4.2 静态方法
- 在开发时,如果需要在类中封装一个方法,这个方法:
- 既不需要访问实例属性或者调用实例方法
- 也不需要访问类属性或者调用类方法
- 这个时候,可以把这个方法封装成一个静态方法
@staticmethoddef 静态方法名():pass
- 静态方法需要用修饰器
@staticmethod来标识,告诉解释器这是一个静态方法 - 通过
类名.调用静态方法
5.小结
- 实例方法——方法内部需要访问实例属性
- 实例方法内部可以使用类名.访问类属性
- 类方法——方法内部只需要访问类属性
- 静态方法——方法内部,不需要访问实例属性和类属性
11 单例
1.单例设计模式
- 单例设计模式
- 目的——让类创建的对象,在系统中只有 唯一的一个实例
- 每一次执行
类名()返回的对象,内存地址是相同的
2.__new__方法
- 使用类名()创建对象时,
Python的解释器首先会调用__new__方法为对象分配空间 __new__是一个由object基类提供的内置的静态方法,主要作用有两个:- 1)在内存中为对象分配空间
- 2)返回对象的引用
Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
重写
__new__方法的代码非常固定!
- 重写
__new__方法一定要return super().__new__(cls) - 否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
- 注意:
__new__是一个静态方法,在调用时需要主动传递cls参数
3.Python中的单例模式
- 单例——让类创建的对象,在系统中 只有 唯一的一个实例
- 定义一个类属性,初始值时
None,用于记录单例对象的引用 - 重写
__new__方法 - 如果类属性
is None,调用父类方法分配空间,并在类属性中记录结果 - 返回类属性中记录的对象引用
- 定义一个类属性,初始值时
只执行一次初始化工作
- 在每次受用
类名()创建对象时,Python的解释器都会自动调用两个方法:__new__分配空间__init__对象初始化
- 在上一小节对
__new__方法改造之后,每次都会得到第一次被创建对象的引用 - 但是:初始化方法还会被再次调用
需求
- 让初始化动作只被执行一次
解决方法
- 定义一个类属性
init_flag标记是否执行过初始化动作,初始值为False - 在
__init__方法中,判断init_flag,如果为False就执行初始化动作 - 然后将
init_flag设置为True - 这样,再次自动调用
__init__方法时,初始化动作就不会被再次执行了
class MusicPlayer(object):instance = Noneinit_flag = Falsedef __new__(cls, *args, **kwargs):if cls.instance is None:cls.instance = super().__new__(cls)return cls.instancedef __init__(self):if MusicPlayer.init_flag:returnprint("初始化播放器")MusicPlayer.init_flag = True
12 异常
1.捕获异常
- 在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加
try(尝试)来捕获异常
try:尝试执行的代码except:出现错误的处理
try尝试,下方编写要尝试代码,不确定是否能够正常执行的代码except如果不是,下方编写尝试失败的代码
2.错误类型捕获
- 在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
try:# 尝试执行的代码passexcept 错误类型1:# 针对错误类型1,对应的代码处理passexcept(错误类型2, 错误类型3):# 针对错误类型2 和 3,对应的代码处理passexcept Exception as result:print("未知错误 %s" % result)
- 当
Python解释器抛出异常时,最后一行错误信息的第一个单词,就是错误类型
3.异常捕获的完整语法
try:# 尝试执行的代码passexcept 错误类型1:# 针对错误类型1,对应的代码处理passexcept 错误类型2:# 针对错误类型2,对应的代码处理passexcept (错误类型3, 错误类型4):# 针对错误类型3 和 4, 对应的代码处理passexcept Exception as result:#打印错误信息print (result)else:# 没有异常才会执行的代码passfinally:# 无论是否有异常都会执行的代码print("无论是否有异常,都会执行的代码")
4.异常的传递
- 异常的传递——当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方
- 如果传递到主程序,仍然没有异常处理,程序才会被终止
提示
- 在开发中,可以在主函数中增加异常捕获
- 而在主函数中调用的其他函数,只要出现异常捕获,都会传递到主函数的异常捕获中
- 这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁
5.抛出raise异常
- 在开发中,除了代码执行出错
Python解释器会抛出异常之外 - 还可以根据应用程序 特有的业务需求 主动抛出异常
Python中提供了一个Exception异常类- 在开发时,如果满足特定业务需求时,希望抛出异常,可以:
- 创建一个
Exception的对象 - 使用
raise关键字抛出异常对象
- 创建一个
def input_password():pwd = input("请输入密码:")if len(pwd) >= 8:return pwdex = Exception("密码长度不够")raise extry:print(input_password())except Exception as result:print(result)
13 模块
1.模块的导入
import 模块1,模块2
import 模块1import 模块2
- 导入之后
- 通过
模块名.,使用模块提供的工具——全局变量、函数、类
- 通过
使用as指定模块的别名
import 模块1 as 模块别名
模块别名应该符合大驼峰命名法
from…import导入
- 如果希望从某一个模块中,导入部分工具,就可以使用
from…import的方式 import 模块名时一次性把模块中所有工具全部导入,并且通过模块名/别名访问
# 从 模块 导入 某一个工具from 模块名1 import 工具名
- 导入之后
- 不需要通过
模块名. - 可以直接使用模块提供的工具——全局变量、函数、类
- 不需要通过
注意
如果两个模块,存在同名的函数,那么后倒入模块的函数,会覆盖掉先导入的函数
# 从 模块 导入 所有工具
from 模块名1 import *
2.模块的搜索顺序
Python的解释器会在导入模块时,会:
- 搜索当前目录指定模块名的文件,如果有就直接导入
- 如果没有,在搜索系统目录
在开发时,给文件起名,不要和系统模块文件 重名
Python中每一个模块都有一个内置属性__file__可以查看模块的完整路径
3.原则——每一个文件都应该是可以被导入的
- 一个独立的
**Python**文件就是一个模块 - 在导入文件时,文件中所有没有任何缩进的代码都会被执行一遍
全局变量、函数、类,注意:直接执行的代码不是向外界提供的工具!
__name__属性
__name__属性可以做到,测试模块的代码只在测试情况下被运行,而在被导入时不会被执行!
__name__是Python的一个内置属性,记录着一个字符串- 如果是被其他文件导入的,
__name__就是模块名 - 如果是当前执行的程序
__name__是__main__
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码的最下方
def main():
# ...
pass
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
14 包(Package)
1.概念
- 包是一个包含多个模块的特殊目录
- 目录下有一个特殊的文件
__init__.py - 包名的命名方式和变量名一直,小写字母+
**_**
好处
- 使用
import 包名可以一次性导入包中的所有模块
2.__init__.py
- 要在外界使用包中的模块,需要在
__init__.py中指定对外界提供的模块列表
# 从 当前目录 导入 模块列表
from . import send_message
from . import receive_message
15 发布模块
1.制作发布压缩包步骤
- 创建
setup.py
from distutils.core import setup
setup(name = "n_message", # 包名
version = "1.0", # 版本
description = "nnniu's 发送和接受信息模块", # 描述信息
long_description = "完整的发送和接受信息模块", # 完整描述信息
author = "nnniu", # 作者
author_email = "nnniu@nnniu.com", # 作者邮箱
url = "www.nnniu.com", # 主页
py_modules = ["n_message.send_message",
"n_message.receive_message"])
- 构建模块:
python3 setup.py build - 生成发布压缩包:
python3 setup.py sdist
2.安装模块
tar -zxvf n_message-1.0.tar.gz
sudo python3 setup.py install
3.卸载模块
直接在安装目录下,把安装模块的目录删除就可以
cd /usr/local/lib/python3.6/dist-packages/
sudo rm -r n_message*
在ipython3中,查找包的.__file__中查看安装位置
import n_message
n_message.__file__
4.pip安装第三方模块
- 第三方模块通常是指由知名的第三方团队 开发的并且被程序员广泛使用的
Python包/模块- 例如
pygame就是一套非常成熟的游戏开发模块
- 例如
pip是一个现代的,通用的Python包管理工具- 提供了对
Python包的查找、下载、安装、卸载等功能
安装和卸载命令
# 将模块安装到 Python 2.x 环境
sudo pip install pygame
sudo pip uninstall pygame
# 将模块安装到 Python 3.x 环境
sudo pip3 install pygame
sudo pip3 uninstall pygame
16 文件
1.文件的基本操作
1.1 操作文件的套路
- 打开文件
- 读、写文件
- 读 将文件内容读入内存
- 写 将内存内容写入文件
- 关闭文件
1.2 操作文件的函数/方法
| 函数/方法 | 说明 |
|---|---|
| open | 打开文件,并且返回文件操作对象 |
| read | 将文件内容读取到内存 |
| write | 将指定内容写入文件 |
| close | 关闭文件 |
open函数负责打开文件,并且返回文件对象read/write/close三个方法都需要通过文件对象来调用
1.3 read方法——读取文件
open函数的第一个参数是要打开的文件名(文件名区分大小写)- 如果文件存在,返回文件操作对象
- 如果文件不存在,会抛出异常
read方法可以一次性读入并返回文件的所有内容close方法负责关闭文件- 如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
- 注意:方法执行后,会把文件指针移动到文件的末尾
file = open("README")
text = file.read()
print(text)
file.close()
- 在开发中,通常会先编写打开和关闭的代码,在编写中概念针对文件的读/写操作
文件指针
- 文件指针标记从哪个位置开始读取数据
- 第一次打开文件时,通常文件指针会指向文件的开始位置
- 当执行了
read方法后,文件指针会移动到读取内容的末尾- 默认情况下会移动到文件末尾
1.4 打开文件的方式
open函数默认以只读方式打开文件,并且返回文件对象- 语法:
f = open (“文件名”,“访问方式”)| 访问方式 | 说明 | | —- | —- | | r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常。 | | w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件。 | | a | 以追加方式打开文件。如果该文件已存在,文件指针放在文件结尾。如果文件不存在,创建新文件进行写入。 | | r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常。 | | w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件。 | | a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件结尾。如果文件不存在,创建新文件进行写入。 |
1.5 按行读取文件内容
readline方法
readline方法可以一次读取一行内容- 方法执行后,会把文件指针移动到下一行,准备再次读取
# 打开文件
file = open("README")
while True:
# 读取一行内容
text = file.readline()
# 判断是否读到内容
if not text:
break
# 每读取一行的末尾已经有了一个'\n'
print(text, end="")
# 关闭文件
file.close()
2.文件/目录的常用管理操作
- 在终端/文件浏览器中可以执行常规的文件/目录管理操作,例如:
- 创建、重命名、删除、改变历经、查看目录内容……
- 在
Python中,如果希望通过程序实现上述功能,需要导入os模块
文件操作
| 方法名 | 说明 | 示例 |
|---|---|---|
| rename | 重命名文件 | os.rename(源文件, 目标文件名) |
| remove | 删除文件 | os.remove(文件名) |
目录操作
| 方法名 | 说明 | 示例 |
|---|---|---|
listdir |
目录列表 | os.listdir(目录名) |
mkdir |
创建目录 | os.mkdir(目录名) |
rmdir |
删除目录 | os.rmdir(目录名) |
getcwd |
获取当前目录 | os.getcwd() |
chdir |
修改工作目录 | os.chdir(目标目录) |
path.isdir |
判断是否是文件 | os.path.isdir(文件路径) |
文件或者目录操作都支持相对路径和绝对路径
3.文本文件的编码格式
- 文本文件存储的内容是基于字符编码的文件,常见的编码有
ASCII编码、UNICODE编码等
Python 2.x 默认使用
ASCII编码Python 3.x 默认使用
UTF-8编码
3.1 ASCII编码和UNICODE编码
ASCII编码
- 计算机中有
256个ASCII字符 - 一个
ASCII在内存中占用1个字节的空间8个0/1的排列组合方式一共有256种,也就是2**8
UTF-8编码格式
- 计算机中使用1~6个字节来标识一个
UTF-8字符,涵盖了地球上几乎所有地区的文字 - 大多数汉字会使用3个字节表示
UTF-8是UNICODE编码的一种编码格式
3.2 Python 2.x 种如何使用中文
- 在Python 2.x 文件的第一行增加以下代码,解释器会以
utf-8编码来处理python文件# *-* coding:utf8 *-*
也可以使用# coding=utf8
unicode字符串
- 在
Python 2.x种,即使制定了文件使用UTF-8的编码格式,但是在遍历字符串时,仍然会以字节为单位遍历字符串 - 要能够正确的遍历字符串,在定义字符串时,需要在字符串的引号前,增加一个小写字母
u,告诉解释器这是一个unicode字符串(使用UTF-8编码格式的字符串)
17 eval函数
eval()函数——将字符串当成有效的表达式来求值并返回计算结果
# 基本的数学计算
In [1]: eval("1 + 1")
Out[1]: 2
# 字符串重复
In [2]: eval("*" * 10)
Out[2]: '**********'
# 将字符串转换成列表
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list
# 将字符串转换成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}")
Out[4]: dict
不要滥用eval
在开发时千万不要使用
eval直接转换input的结果
__import__(‘os’).system(‘ls’)
- 等价代码
import os
os.system("终端命令")
- 执行成功,返回0
- 执行失败,返回错误信息
