01 面向对象基础语法

dir内置函数

Python中可以使用以下两个方法验证:

  1. 标识符/数据后输入一个.,然后按下TAB键,iPython会提示该对象能够调用的方法列表
  2. 使用内置函数dir传入标识符/数据,可以查看对象内的所有属性及方法

提示__方法名__格式的方法是Python提供的内置方法/属性dir()函数可查看

方法名 类型 作用
__new__ 方法 创建对象时,会被自动调用
__init__ 方法 对象被初始化时,会被自动调用
__del__ 方法 对象被从内存中销毁前,会被自动调用
__str__ 方法 返回对象的描述信息,print函数输出使用

02 定义简单的类

1.定义只包含方法的类

  1. class 类名:
  2. def 方法1(self, 参数列表):
  3. pass
  4. def 方法2(self, 参数列表):
  5. pass
  • 方法的定义格式和之前学习过的函数几乎一样
  • 区别在于第一个参数必须是self
  • 类名的命名规则要符合大驼峰命名法

2.创建对象

  • 格式:对象变量 = 类名()

%d可以以10进制输出数字

%x可以以16进制输出数字

03 方法中的self函数

1.给对象增加属性

  • Python中,要给对象设置属性,只需要在类的外部代码中直接通过.设置一个属性即可(不推荐使用)

2.使用self方法

  • 哪一个对象调用的方法,方法内的self就是哪一个对象的引用
  • 在类封装的方法内部,self就表示当前调用方法的对象自己
  • 调用方法时,程序员不需要传递self参数
  • 在方法内部
    • 可以通过self.访问对象的属性
    • 也可以通过self.调用其他的对象方法

04 初始化方法

1.初始化方法init

  • 在使用类名()创建对象时,会自动执行以下操作:
    1. 为对象在内存中分配空间——创建对象
    2. 为对象的属性设置初始值——初始化方法(init)
  • 这个初始化方法就是__init__方法,__init__是对象的内置方法

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

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

  • __init__方法内部使用self.属性名 = 属性的初始值就可以定义属性
  • 定义属性之后,再使用Cat类创建的对象,都会拥有该属性

3.初始化同时设置初始值

  • 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__方法进行改造
    1. 把希望设置的属性值,定义成__init__方法的参数
    2. 在方法内部使用self.属性 = 形参接受外部传递的参数
    3. 在创建对象时,使用类名(属性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 继承的语法

  1. class 类名(父类名):
  2. pass
  • Dog类是Animal类的子类Animal类是Dog类的父类Dog类从Animal继承
  • Dog类是Animal类的派生类Animal类是Dog类的基类Dog类从Animal派生
  • 子类拥有父类以及父类的父类中封装的所有属性方法

1.2 方法的重写

1.2.1 覆盖子类的方法
  • 子类中定义一个和父类同名的方法并且实现

1.2.2 对父类方法进行扩展
  1. 在子类中 重写 父类的方法
  2. 在需要的位置使用super().父类方法来调用父类方法的执行
  3. 代码其他的位置针对子类的需求,编写子类特有的代码实现

关于super
  • Pythonsuper是一个特殊的类
  • super()就是使用super类创建出来的对象
  • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
  1. class XiaoTianQuan(Dog):
  2. def bark(self):
  3. print("bark like god~~")
  4. super().bark()
  5. # Dog.bark(self) 不推荐使用

1.2.3 调用父类方法的另外一种形式

父类名.方法(self)

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

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

2.多继承

2.1 多继承的语法

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

2.2 Python中的MRO —— 方法搜索顺序

  • Python中针对提供了一个内置属性__mro__可以查看方法搜索顺序
  • MROmethod resolution order,主要用于在多继承时判断 方法、属性 的调用 路径
    print(C.__mro__)
  • 在搜索方法时,是按照__mro__的输出结果从左至右的顺序查找的
  • 如果在当前类中找到方法,就直接执行,不在搜索
  • 如果没有找到,就查找下一个类中是否有对性的方法,如果找到,就直接执行,不在搜索
  • 如果找到最后一个类,还没有找到方法,程序报错

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

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

  • 新式类:以object为基类的类
  • 旧式类:不以object为基类的类
  • Python 3.x中定义类时,如果没有指定父类,会默认使用object作为该类的基类——Python 3.x中定义的类都是新式类

为了保证编写的代码能够同时在Python 2.xPython 3.x运行!

今后在定义类时,如果没有父类,建议统一继承自object

  1. class 类名(object):
  2. pass

09 多态

不同的子类对象调用相同的父类方法,产生不同的执行结果

  • 多态可以增加代码的灵活度
  • 继承重写父类方法为前提
  • 时调用方法的技巧,不会影响到类的内部设计

10 类的结构

1.实例

  1. 使用面向对象开发,第1步时设计
  2. 使用类名()创建对象,**创建对象的动作有两步:
    • 在内存中为对象分配空间
    • 调用初始化方法__init__对象初始化
  3. 对象创建后,内存中就有了一个对象的实实在在的存在——实例
  • 每一个对象都有自己独立的内存空间保存各自不同的属性
  • 多个对象的方法在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

2.类是一个特殊的对象

Python一切及对象

  • class AAA:定义的类属于类对象
  • obj1 = AAA()属于实例对象
  • 通过类名.的方式可以访问类的属性或者调用类的方法

3.类属性和实例属性

3.1 类属性的定义

  • 类属性就是给类对象中定义的属性
  • 通常用来记录与这个类相关的特征
  • 类属性 不会用于记录具体对象的特征
  1. class Tool(object):
  2. # 类属性
  3. count = 0
  4. # 实例属性
  5. def __init__(self, name):
  6. self.name = name
  7. Tool.count += 1

3.2 属性的获取机制

  • Python属性的获取存在一个向上查找的机制
  1. # 首先在对象内部查找对象属性,查找不到向上查找类属性
  2. tool1.count

4.类方法和静态方法

4.1 类方法

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

4.2 静态方法

  • 在开发时,如果需要在中封装一个方法,这个方法:
    • 不需要访问实例属性或者调用实例方法
    • 不需要访问类属性或者调用类方法
  • 这个时候,可以把这个方法封装成一个静态方法
  1. @staticmethod
  2. def 静态方法名():
  3. pass
  • 静态方法需要用修饰器@staticmethod来标识,告诉解释器这是一个静态方法
  • 通过类名.调用静态方法

5.小结

  1. 实例方法——方法内部需要访问实例属性
    • 实例方法内部可以使用类名.访问类属性
  2. 类方法——方法内部需要访问类属性
  3. 静态方法——方法内部,不需要访问实例属性类属性

11 单例

1.单例设计模式

  • 单例设计模式
    • 目的——让创建的对象,在系统中只有 唯一的一个实例
    • 每一次执行类名()返回的对象,内存地址是相同的

2.__new__方法

  • 使用类名()创建对象时,Python的解释器首先会调用__new__方法为对象分配空间
  • __new__是一个由object基类提供的内置的静态方法,主要作用有两个:
    • 1)在内存中为对象分配空间
    • 2)返回对象的引用
  • Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法

重写__new__方法的代码非常固定!

  • 重写__new__方法一定要return super().__new__(cls)
  • 否则Python的解释器得不到分配了空间的对象引用就不会调用对象的初始化方法
  • 注意:__new__是一个静态方法,在调用时需要主动传递cls参数

3.Python中的单例模式

  • 单例——让创建的对象,在系统中 只有 唯一的一个实例
    1. 定义一个类属性,初始值时None,用于记录单例对象的引用
    2. 重写__new__方法
    3. 如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
    4. 返回类属性中记录的对象引用

只执行一次初始化工作

  • 在每次受用类名()创建对象时,Python的解释器都会自动调用两个方法:
    • __new__分配空间
    • __init__对象初始化
  • 在上一小节对__new__方法改造之后,每次都会得到第一次被创建对象的引用
  • 但是:初始化方法还会被再次调用

需求
  • 初始化动作只被执行一次

解决方法
  1. 定义一个类属性init_flag标记是否执行过初始化动作,初始值为False
  2. __init__方法中,判断init_flag,如果为False就执行初始化动作
  3. 然后将init_flag设置为True
  4. 这样,再次自动调用__init__方法时,初始化动作就不会被再次执行了
  1. class MusicPlayer(object):
  2. instance = None
  3. init_flag = False
  4. def __new__(cls, *args, **kwargs):
  5. if cls.instance is None:
  6. cls.instance = super().__new__(cls)
  7. return cls.instance
  8. def __init__(self):
  9. if MusicPlayer.init_flag:
  10. return
  11. print("初始化播放器")
  12. MusicPlayer.init_flag = True

12 异常

1.捕获异常

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

2.错误类型捕获

  • 在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常做出不同的响应,这个时候,就需要捕获错误类型了
  1. try:
  2. # 尝试执行的代码
  3. pass
  4. except 错误类型1:
  5. # 针对错误类型1,对应的代码处理
  6. pass
  7. except(错误类型2, 错误类型3):
  8. # 针对错误类型2 和 3,对应的代码处理
  9. pass
  10. except Exception as result:
  11. print("未知错误 %s" % result)
  • Python解释器抛出异常时,最后一行错误信息的第一个单词,就是错误类型

3.异常捕获的完整语法

  1. try:
  2. # 尝试执行的代码
  3. pass
  4. except 错误类型1:
  5. # 针对错误类型1,对应的代码处理
  6. pass
  7. except 错误类型2:
  8. # 针对错误类型2,对应的代码处理
  9. pass
  10. except (错误类型3, 错误类型4):
  11. # 针对错误类型3 和 4, 对应的代码处理
  12. pass
  13. except Exception as result:
  14. #打印错误信息
  15. print (result)
  16. else:
  17. # 没有异常才会执行的代码
  18. pass
  19. finally:
  20. # 无论是否有异常都会执行的代码
  21. print("无论是否有异常,都会执行的代码")

4.异常的传递

  • 异常的传递——当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方
  • 如果传递到主程序,仍然没有异常处理,程序才会被终止

提示

  • 在开发中,可以在主函数中增加异常捕获
  • 而在主函数中调用的其他函数,只要出现异常捕获,都会传递到主函数的异常捕获
  • 这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁

5.抛出raise异常

  • 在开发中,除了代码执行出错Python解释器会抛出异常之外
  • 还可以根据应用程序 特有的业务需求 主动抛出异常
  • Python中提供了一个Exception异常类
  • 在开发时,如果满足特定业务需求时,希望抛出异常,可以:
    1. 创建一个Exception对象
    2. 使用raise关键字抛出异常对象
  1. def input_password():
  2. pwd = input("请输入密码:")
  3. if len(pwd) >= 8:
  4. return pwd
  5. ex = Exception("密码长度不够")
  6. raise ex
  7. try:
  8. print(input_password())
  9. except Exception as result:
  10. print(result)

13 模块

1.模块的导入

  1. import 模块1,模块2
  1. import 模块1
  2. import 模块2
  • 导入之后
    • 通过模块名.,使用模块提供的工具——全局变量、函数、类

使用as指定模块的别名

import 模块1 as 模块别名

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

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

注意

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

# 从 模块 导入 所有工具
from 模块名1 import *

2.模块的搜索顺序

Python的解释器会在导入模块时,会:

  1. 搜索当前目录指定模块名的文件,如果有就直接导入
  2. 如果没有,在搜索系统目录

在开发时,给文件起名,不要和系统模块文件 重名

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.制作发布压缩包步骤

  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"])

有关字典的详细信息

  1. 构建模块:python3 setup.py build
  2. 生成发布压缩包: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. 读、写文件
    • 将文件内容读入内存
    • 将内存内容写入文件
  3. 关闭文件

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编码
  • 计算机中有256ASCII字符
  • 一个ASCII在内存中占用1个字节的空间
    • 80/1的排列组合方式一共有256种,也就是2**8

UTF-8编码格式
  • 计算机中使用1~6个字节来标识一个UTF-8字符,涵盖了地球上几乎所有地区的文字
  • 大多数汉字会使用3个字节表示
  • UTF-8UNICODE编码的一种编码格式

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
  • 执行失败,返回错误信息