文章目录
1.面向对象(OPP)的基本概念
1.1 面相过程
- 把完成某一个需求的
所有步骤
从头到尾
逐步实现 - 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
- 最后完成的代码,就是顺序地调用 不同的函数
特点
- 注重 步骤与过程,不注重职责分工
- 如果需求复杂,代码会变得很复杂
- 开发复杂项目,没有固定的套路,开发难度很大!
1.2 面向对象
相比较函数,面向对象 是 更大 的 封装,根据 职责 在 一个对象中 封装 多个方法
面向对象编程 —— Object Oriented Programming
简写 OOP
- 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)
- 根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
- 最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法
特点
- 注重 对象和职责,不同的对象承担不同的职责
- 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
- 需要在面向过程基础上,再学习一些面向对象的语法
2.类与对象
2.1 类和对象的概念
类 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用
- 特征 被称为 属性
- 行为 被称为 方法
对象 是 由类创建出来的一个具体存在,可以直接使用,由 哪一个类 创建出来的 对象,就拥有在 哪一个类 中定义的:
- 属性
- 方法
2.2 类和对象的关系
- 类是模板,对象 是根据 类 这个模板创建出来的,应该 先有类,再有对象
- 类 只有一个,而 对象 可以有很多个
- 不同的对象 之间 属性 可能会各不相同
- 类 中定义了什么 属性和方法,对象 中就有什么属性和方法,不可能多,也不可能少
3.面向对象基础语法
3.1 dir内置函数
查看对象的方法列表
- 在 标识符 / 数据 后输入一个
.
,然后按下TAB
键,iPython
会提示该对象能够调用的 方法列表 - 使用内置函数
dir
传入 标识符 / 数据,可以查看对象内的 所有属性及方法
__方法名__
格式的方法是 Python
提供的 内置方法 / 属性
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __new__ | 方法 | 创建对象时,会被 自动 调用 |
02 | __init__ | 方法 | 对象被初始化时,会被 自动 调用 |
03 | __del__ | 方法 | 对象被从内存中销毁前,会被 自动 调用 |
04 | __str__ | 方法 | 返回对象的描述信息,print 函数输出使用 |
3.2 定义简单的类
定义类的语法格式:
class 类名:
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
创建对象的语法格式:
对象变量 = 类名()
在 Python
中使用类 创建对象之后,tom
变量中 仍然记录的是 对象在内存中的地址
使用 print
输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
%d
可以以 10 进制 输出数字%x
可以以 16 进制 输出数字
3.3 方法中的self参数
在类封装的方法内部,self
就表示 当前调用方法的对象自己
调用方法时,程序员不需要传递 self
参数
在方法内部
- 可以通过
self.
访问对象的属性 - 也可以通过
self.
调用其他的对象方法
class Cat:
def eat(self):
print("%s 爱吃鱼" % self.name)
tom = Cat()
tom.name = "Tom"
tom.eat()
- 在 类的外部,通过
变量名.
访问对象的 属性和方法 - 在 类封装的方法中,通过
self.
访问对象的 **属性和方法
3.4 初始化方法
当使用 类名()
创建对象时,会 自动 执行以下操作:
- 为对象在内存中 分配空间 —— 创建对象
- 为对象的属性 设置初始值 —— 初始化方法(
init
)
这个 初始化方法 就是__init__
方法,__init__
是对象的内置方法, 专门 用来定义一个类 具有哪些属性!
在 __init__
方法内部使用 self.属性名 = 属性的初始值
就可以 定义属性
class Cat:
def __init__(self):
print("这是一个初始化方法")
# 定义用 Cat 类创建的猫对象都有一个 name的属性
self.name = "Tom"
def eat(self):
print("%s 爱吃鱼" % self.name)
在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 __init__
方法进行 改造
- 把希望设置的属性值,定义成
__init__
方法的参数 - 在方法内部使用
self.属性 = 形参
接收外部传递的参数 - 在创建对象时,使用
类名(属性1, 属性2...)
调用
class Cat:
def __init__(self, name):
print("初始化方法 %s" % name)
self.name = name
...
tom = Cat("Tom")
...
3.5 内置方法和属性
3.5.1 __del__
方法
在 Python
中
- 当使用
类名()
创建对象时,为对象 分配完空间后,自动 调用__init__
方法 - 当一个 对象被从内存中销毁 前,会 自动 调用
__del__
方法
class Cat:
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 去了" % self.name)
# tom 是一个全局变量
tom = Cat("Tom")
print(tom.name)
# del 关键字可以删除一个对象
del tom
3.5.2 __str__
方法
在 Python
中,使用 print
输出 对象变量,默认情况下,会输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址
如果在开发中,希望使用 print
输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__
这个内置方法了
注意:
__str__
方法必须返回一个字符串
class Cat:
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 去了" % self.name)
def __str__(self):
return "我是小猫:%s" % self.name
tom = Cat("Tom")
print(tom)
4.面向对象封装
4.1 封装
- 封装 是面向对象编程的一大特点
- 面向对象编程的 第一步 —— 将 属性 和 方法 封装 到一个抽象的 类 中
- 外界 使用 类 创建 对象,然后 让对象调用方法
- 对象方法的细节 都被 封装 在 类的内部
4.2 案例——摆放家具
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return "[%s] 占地面积 %.2f" % (self.name, self.area)
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
# 剩余面积默认和总面积一致
self.free_area = area
# 默认没有任何的家具
self.item_list = []
def __str__(self):
# Python 能够自动的将一对括号内部的代码连接在一起
return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s"
% (self.house_type, self.area,
self.free_area, self.item_list))
def add_item(self, item):
print("要添加 %s" % item)
# 1. 判断家具面积是否大于剩余面积
if item.area > self.free_area:
print("%s 的面积太大,不能添加到房子中" % item.name)
return
# 2. 将家具的名称追加到名称列表中
self.item_list.append(item.name)
# 3. 计算剩余面积
self.free_area -= item.area
...
# 2. 创建房子对象
my_home = House("两室一厅", 60)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
4.3 案例——士兵突击
class Gun:
def __init__(self, model):
# 枪的型号
self.model = model
# 子弹数量
self.bullet_count = 0
def add_bullet(self, count):
self.bullet_count += count
def shoot(self):
# 判断是否还有子弹
if self.bullet_count <= 0:
print("没有子弹了...")
return
# 发射一颗子弹
self.bullet_count -= 1
print("%s 发射子弹[%d]..." % (self.model, self.bullet_count))
# 创建枪对象
ak47 = Gun("ak47")
ak47.add_bullet(50)
在定义属性时,如果 不知道设置什么初始值,可以设置为 None
None
关键字 表示 什么都没有- 表示一个 空对象,没有方法和属性,是一个特殊的常量
- 可以将
None
赋值给任何一个变量
class Soldier:
def __init__(self, name):
# 姓名
self.name = name
# 枪,士兵初始没有枪 None 关键字表示什么都没有
self.gun = None
def fire(self):
# 1. 判断士兵是否有枪
if self.gun is None:
print("[%s] 还没有枪..." % self.name)
return
# 2. 让枪装填子弹
self.gun.add_bullet(50)
# 3. 让枪发射子弹
self.gun.shoot()
4.4 身份运算符
身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用
在 Python
中针对 None
比较时,建议使用 is
判断
运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用同一个对象 | x is y,类似 id(x) == id(y) |
is not | is not 是判断两个标识符是不是引用不同对象 | x is not y,类似 id(a) != id(b) |
is 与 == 区别:
is
用于判断 两个变量 引用对象是否为同一个
==
用于判断 引用变量的值 是否相等
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> b is a
False
>>> b == a
True
5.私有属性和私有方法
语法格式:
在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是 私有 属性或方法
class Women:
def __init__(self, name):
self.name = name
# 不要问女生的年龄
self.__age = 18
def __secret(self):
print("我的年龄是 %d" % self.__age)
xiaofang = Women("小芳")
# 私有属性,外部不能直接访问
# print(xiaofang.__age)
# 私有方法,外部不能直接调用
# xiaofang.__secret()
Python
中,并没有 真正意义 的 私有
- 在给 属性、方法 命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
- 处理方式:在 名称 前面加上
_类名
=>_类名__名称
print(xiaofang._Women__age)
xiaofang._Women__secret()
6.继承
6.1 单继承
子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法
class 类名(父类名):
pass
Dog
类是 Animal
类的子类\派生类,Animal
类是 Dog
类的父类\基类,Dog
类从 Animal
类继承\派生
6.1.1 方法的重写
- 覆盖:在 子类中 定义了一个 和父类同名的方法并且实现
- 扩展:在需要的位置使用
super().父类方法
来调用父类方法的执行,代码其他的位置针对子类的需求,编写 子类特有的代码实现
super:
在 Python
中 super
是一个 特殊的类,super()
就是使用 super
类创建出来的对象, 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现
调用父类方法的另外一种方式
在
Python 2.x
时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
这种方式,目前在 Python 3.x
还支持这种方式,这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
提示:
在开发时,父类名
和 super()
两种方式不要混用
6.1.2 父类的私有属性和方法
- 子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
- 子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法
6.2 多继承
如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承
6.2.1 MRO——方法搜索顺序
Python
中针对 类 提供了一个 内置属性__mro__
可以查看 方法 搜索顺序- MRO 是
method resolution order
,主要用于 在多继承时判断 方法、属性 的调用 路径
print(C.__mro__)
输出结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
- 在搜索方法时,是按照
__mro__
的输出结果 从左至右 的顺序查找的 - 如果在当前类中 找到方法,就直接执行,不再搜索
- 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
- 如果找到最后一个类,还没有找到方法,程序报错
6.2.2 新式类与旧式(经典)类
object
是Python
为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir
函数查看
新式类:以 object
为基类的类,推荐使用
经典类:不以 object
为基类的类,不推荐使用
- 在
Python 3.x
中定义类时,如果没有指定父类,会 默认使用object
作为该类的 基类 ——Python 3.x
中定义的类都是 新式类 - 在
Python 2.x
中定义类时,如果没有指定父类,则不会以object
作为 基类
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在 Python 2.x
和 Python 3.x
运行,今后在定义类时如果没有父类,建议统一继承自 object
class 类名(object):
pass
7.多态
多态 :不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
- 多态 可以 增加代码的灵活度
- 以 继承 和 重写父类方法 为前提
- 是调用方法的技巧,不会影响到类的内部设计
Person
类中只需要让 狗对象 调用 game
方法,而不关心具体是 什么狗,game
方法是在 Dog
父类中定义的, 在程序执行时,传入不同的 狗对象 实参,就会产生不同的执行效果
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print("%s 蹦蹦跳跳的玩耍..." % self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s 飞到天上去玩耍..." % self.name)
class Person(object):
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
# 让狗玩耍
dog.game()
# 1. 创建一个狗对象
wangcai = XiaoTianDog("飞天旺财")
# 2. 创建一个小明对象
xiaoming = Person("小明")
# 3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)
8.类属性和类方法
8.1 类的结构
- 创建出来的 对象 叫做 类 的 实例
- 创建对象的 动作 叫做 实例化
- 对象的属性 叫做 实例属性
- 对象调用的方法 叫做 实例方法
在程序运行时,类 同样 会被加载到内存,在 Python
中,类 是一个特殊的对象 —— 类对象。在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
除了封装 实例 的 属性 和 方法外,类对象 还可以拥有自己的 属性 和 方法,通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
- 类属性
- 类方法
8.2 类属性和实例属性
类属性 就是给 类对象 中定义的 属性,通常用来记录 与这个类相关 的特征
class Tool(object):
# 使用赋值语句,定义类属性,记录创建工具对象的总数
count = 0
def __init__(self, name):
self.name = name
# 针对类属性做一个计数+1
Tool.count += 1
# 创建工具对象
tool1 = Tool("斧头")
# 知道使用 Tool 类到底创建了多少个对象?
print("现在创建了 %d 个工具" % Tool.count)
属性的获取机制:
在 Python
中 属性的获取 存在一个 向上查找机制
因此,要访问类属性有两种方式:
- 类名.类属性
- 对象.类属性 (不推荐)
注意:
如果使用 对象.类属性 = 值
赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值
8.3 类方法和静态方法
8.3.1 类方法
类方法 就是针对 类对象 定义的方法,在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法
语法如下
@classmethod
def 类方法名(cls):
pass
-
类方法需要用 修饰器
@classmethod
来标识,告诉解释器这是一个类方法 -
类方法的 第一个参数 应该是
cls
1)由 **哪一个类** 调用的方法,方法内的 `cls` 就是 **哪一个类的引用** 2)这个参数和 **实例方法** 的第一个参数是 `self` 类似 3)**提示** 使用其他名称也可以,不过习惯使用 `cls`
-
通过 类名. 调用 类方法,调用方法时,不需要传递
cls
参数 -
在方法内部
- 可以通过
cls.
访问类的属性 - 也可以通过
cls.
调用其他的类方法
- 可以通过
@classmethod
def show_tool_count(cls):
"""显示工具对象的总数"""
print("工具对象的总数 %d" % cls.count)
8.3.2 静态方法
在开发时,如果需要在 类 中封装一个方法,这个方法:
不需要 访问 实例属性 或者调用 实例方法,也 不需要 访问 类属性 或者调用 类方法
这个时候,可以把这个方法封装成一个 静态方法
语法如下
@staticmethod
def 静态方法名():
pass
- 静态方法 需要用 修饰器
@staticmethod
来标识,告诉解释器这是一个静态方法 - 通过 类名. 调用 静态方法
class Dog(object):
dog_count = 0
@staticmethod
def run():
# 不需要访问实例属性也不需要访问类属性的方法
print("狗在跑...")
def __init__(self, name):
self.name = name
8.3.3 方法综合案例
class Game(object):
# 游戏最高分,类属性
top_score = 0
# 实例属性
def __init__(self, player_name):
self.player_name = player_name
@staticmethod
def show_help():
print("帮助信息:让僵尸走进房间")
@classmethod
def show_top_score(cls):
print("游戏最高分是 %d" % cls.top_score)
def start_game(self):
print("[%s] 开始游戏..." % self.player_name)
# 使用类名.修改历史最高分
Game.top_score = 999
# 1. 查看游戏帮助
Game.show_help()
# 2. 查看游戏最高分
Game.show_top_score()
# 3. 创建游戏对象,开始游戏
game = Game("小明")
game.start_game()
# 4. 游戏结束,查看游戏最高分
Game.show_top_score()
9.单例
9.1 单例设计模式
单例设计模式
-
目的 —— 让 类 创建的对象,在系统中 只有 唯一的一个实例
-
每一次执行
类名()
返回的对象,内存地址是相同的单例设计模式的应用场景: 音乐播放 对象 回收站 对象 打印机 对象 ……
9.2 __new__
方法
- 使用 类名() 创建对象时,
Python
的解释器 首先 会 调用__new__
方法为对象 分配空间 __new__
是一个 由object
基类提供的 内置的静态方法,主要作用有两个:
- 在内存中为对象 分配空间
- 返回 对象的引用
Python
的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给__init__
方法,重写__new__
方法 一定要return super().__new__(cls)
,否则 Python 的解释器 得不到 分配了空间的 对象引用,就不会调用对象的初始化方法- 注意:
__new__
是一个静态方法,在调用时需要 主动传递cls
参数
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __init__(self):
print("初始化音乐播放对象")
player = MusicPlayer()
print(player)
9.3 Python 中的单例
- 定义一个 类属性,初始值是
None
,用于记录 单例对象的引用 - 重写
__new__
方法 - 如果 类属性
is None
,调用父类方法分配空间,并在类属性中记录结果 - 返回 类属性 中记录的 对象引用
class MusicPlayer(object):
# 定义类属性记录单例对象引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否已经被赋值
if cls.instance is None:
cls.instance = super().__new__(cls)
# 2. 返回类属性的单例引用
return cls.instance
在每次使用 类名()
创建对象时,Python
的解释器都会自动调用两个方法:
__new__
分配空间__init__
对象初始化
在上一小节对__new__
方法改造之后,每次都会得到 第一次被创建对象的引用,但是:初始化方法还会被再次调用
需求: 让 初始化动作 只被 执行一次
解决办法
- 定义一个类属性
init_flag
标记是否 执行过初始化动作,初始值为False
- 在
__init__
方法中,判断init_flag
,如果为False
就执行初始化动作 - 然后将
init_flag
设置为True
- 这样,再次 自动 调用
__init__
方法时,初始化动作就不会被再次执行 了
class MusicPlayer(object)
# 记录第一个被创建对象的引用
instance = None
# 记录是否执行过初始化动作
init_flag = False
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否是空对象
if cls.instance is None:
# 2. 调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3. 返回类属性保存的对象引用
return cls.instance
def __init__(self):
if not MusicPlayer.init_flag:
print("初始化音乐播放器")
MusicPlayer.init_flag = True
# 创建多个对象
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
10.异常
程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性
10.1 捕获异常
在程序开发中,如果 对某些代码的执行不能确定是否正确,可以增加 try(尝试)
来 捕获异常
try:
尝试执行的代码
except:
出现错误的处理
在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except (错误类型2, 错误类型3):
# 针对错误类型2 和 3,对应的代码处理
pass
except Exception as result:
print("未知错误 %s" % result)
示例:要求用户输入整数
try:
num = int(input("请输入整数:"))
result = 8 / num
print(result)
except ValueError:
print("请输入正确的整数")
except ZeroDivisionError:
print("除 0 错误")
捕获未知错误
如果希望程序 无论出现任何错误,都不会因为 Python
解释器 抛出异常而被终止,可以再增加一个 except
except Exception as result:
print("未知错误 %s" % result)
异常捕获完整格式
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("无论是否有异常,都会执行的代码")
10.2 异常的传递
在开发中,可以在主函数中增加 异常捕获,而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的 异常捕获 中,这样就不需要在代码中,增加大量的 异常捕获,能够保证代码的整洁
def demo1():
return int(input("请输入一个整数:"))
def demo2():
return demo1()
try:
print(demo2())
except ValueError:
print("请输入正确的整数")
except Exception as result:
print("未知错误 %s" % result)
10.3 抛出raise异常
在开发中,除了 代码执行出错 Python
解释器会 抛出 异常之外,还可以根据 应用程序 特有的业务需求 主动抛出异常
Python
中提供了一个 Exception
异常类,在开发时,如果满足 特定业务需求时,希望 抛出异常,可以:
- 创建 一个
Exception
的 对象 - 使用
raise
关键字 抛出 异常对象
def input_password():
# 1. 提示用户输入密码
pwd = input("请输入密码:")
# 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码
if len(pwd) >= 8:
return pwd
# 3. 密码长度不够,需要抛出异常
# 1> 创建异常对象 - 使用异常的错误信息字符串作为参数
ex = Exception("密码长度不够")
# 2> 抛出异常对象
raise ex
try:
user_pwd = input_password()
print(user_pwd)
except Exception as result:
print("发现错误:%s" % result)
11.模块和包
11.1 模块
11.1.1 导入模块
每一个以扩展名 py
结尾的 Python
源代码文件都是一个 模块
import 模块名1, 模块名2
import 模块名1
import 模块名2
import 模块名1 as 模块别名 # 用as指定模块的名称
from 模块名1 import 工具名
from 模块名1 import *
#如果两个模块,存在同名的函数,那么后导入模块的函数,会覆盖掉先导入的函数,
#可以使用 as 关键字给其中一个工具起一个别名
11.1.2 模块的搜索顺序
Python
的解释器在 导入模块 时,会:
- 搜索 当前目录 指定模块名的文件,如果有就直接导入
- 如果没有,再搜索 系统目录
Python
中每一个模块都有一个内置属性 __file__
可以 查看模块 的 完整路径
import random
rand = random.randint(0, 10)
print(rand)
11.1.3 文件导入原则
在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍
实际开发场景
在实际开发中,开发人员 通常会在 模块下方 增加一些测试代码, 仅在模块内使用,而被导入到其他文件中不需要执行
__name__
属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行!
__name__
是 Python
的一个内置属性,记录着一个 字符串
- 如果 是被其他文件导入的,
__name__
就是 模块名 - 如果 是当前执行的程序
__name__
是__main__
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码的最下方
def main():
# ...
pass
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
11.2 包
包 是一个 包含多个模块 的 特殊目录,目录下有一个 特殊的文件 __init__.py
包名的 命名方式 和变量名一致,小写字母 + _
好处:使用 import 包名
可以一次性导入 包 中 所有的模块
__init__.py
:要在外界使用 包 中的模块,需要在 __init__.py
中指定 对外界提供的模块列表
# 从 当前目录 导入 模块列表
from . import send_message
from . import receive_message
11.3发布模块
11.3.1 制作发布压缩包步骤
- 创建 setup.py
from distutils.core import setup
setup(name="hm_message", # 包名
version="1.0", # 版本
description="rt's 发送和接收消息模块", # 描述信息
long_description="完整的发送和接收消息模块", # 完整描述信息
author="rt", # 作者
author_email="rt@qq.com", # 作者邮箱
url="www.rt.com", # 主页
py_modules=["hm_message.send_message",
"hm_message.receive_message"])
有关字典参数的详细信息,可以参阅官方网站
- 构建模块
$ python3 setup.py build
- 生成发布压缩包
$ python3 setup.py sdist
注意:要制作哪个版本的模块,就使用哪个版本的解释器执行!
11.3.2 安装模块
$ tar -zxvf hm_message-1.0.tar.gz
$ sudo python3 setup.py install
卸载模块
直接从安装目录下,把安装模块的 目录 删除就可以
$ cd /usr/local/lib/python3.5/dist-packages/
$ sudo rm -r hm_message*
11.3.3 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
在 Mac
下安装 iPython
$ sudo pip install ipython
在 Linux
下安装 iPython
$ sudo apt install ipython
$ sudo apt install ipython3
12.文件
12.1 文件的基本操作
在 Python
中要操作文件需要记住 1 个函数和 3 个方法
序号 | 函数/方法 | 说明 |
---|---|---|
01 | open | 打开文件,并且返回文件操作对象 |
02 | read | 将文件内容读取到内存 |
03 | write | 将指定内容写入文件 |
04 | close | 关闭文件 |
open
函数负责打开文件,并且返回文件对象read
/write
/close
三个方法都需要通过 文件对象 来调用
12.1.1 read
open
函数的第一个参数是要打开的文件名(文件名区分大小写)
- 如果文件 存在,返回 文件操作对象
- 如果文件 不存在,会 抛出异常
read
方法可以一次性 读入 并 返回 文件的 所有内容
注意:read
方法执行后,会把 文件指针 移动到 文件的末尾
close
方法负责 关闭文件
如果 忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
# 1. 打开 - 文件名需要注意大小写
file = open("README")
# 2. 读取
text = file.read()
print(text)
# 3. 关闭
file.close()
提示:在开发中,通常会先编写 打开 和 关闭 的代码,再编写中间针对文件的 读/写 操作
按行读取文件内容
read
方法默认会把文件的 所有内容 一次性读取到内存
如果文件太大,对内存的占用会非常严重
readline
方法
readline
方法可以一次读取一行内容- 方法执行后,会把 文件指针 移动到下一行,准备再次读取
# 打开文件
file = open("README")
while True:
# 读取一行内容
text = file.readline()
# 判断是否读到内容
if not text:
break
# 每读取一行的末尾已经有了一个 `\n`
print(text, end="")
# 关闭文件
file.close()
12.1.2 open
open
函数默认以 只读方式 打开文件,并且返回文件对象
f = open("文件名", "访问方式")
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
提示: 频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以 只读、只写 的方式来操作文件
写入文件示例
# 打开文件
f = open("README", "w")
f.write("hello python!\n")
f.write("今天天气真好")
# 关闭文件
f.close()
12.1.3 文件读写案例 —— 复制文件
小文件复制
打开一个已有文件,读取完整内容,并写入到另外一个文件
# 1. 打开文件
file_read = open("README")
file_write = open("README[复件]", "w")
# 2. 读取并写入文件
text = file_read.read()
file_write.write(text)
# 3. 关闭文件
file_read.close()
file_write.close()
大文件复制
打开一个已有文件,逐行读取内容,并顺序写入到另外一个文件
# 1. 打开文件
file_read = open("README")
file_write = open("README[复件]", "w")
# 2. 读取并写入文件
while True:
# 每次读取一行
text = file_read.readline()
# 判断是否读取到内容
if not text:
break
file_write.write(text)
# 3. 关闭文件
file_read.close()
file_write.close()
12.2 文件/目录的常用管理操作
在 终端 / 文件浏览器、 中可以执行常规的 文件 / 目录 管理操作,例如:
创建、重命名、删除、改变路径、查看目录内容、……
在 Python
中,如果希望通过程序实现上述功能,需要导入 os
模块
12.2.1 文件操作
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | rename | 重命名文件 | os.rename(源文件名, 目标文件名) |
02 | remove | 删除文件 | os.remove(文件名) |
12.2.2 目录操作
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | listdir | 目录列表 | os.listdir(目录名) |
02 | mkdir | 创建目录 | os.mkdir(目录名) |
03 | rmdir | 删除目录 | os.rmdir(目录名) |
04 | getcwd | 获取当前目录 | os.getcwd() |
05 | chdir | 修改工作目录 | os.chdir(目标目录) |
06 | path.isdir | 判断是否是文件 | os.path.isdir(文件路径) |
提示:文件或者目录操作都支持 相对路径 和 绝对路径
12.3 文本文件的编码格式
Python 2.x 默认使用
ASCII
编码格式
Python 3.x 默认使用UTF-8
编码格式
12.3.1 ASCII
编码
计算机中只有 256
个 ASCII
字符,一个 ASCII
在内存中占用 1 个字节 的空间
8
个 0/1
的排列组合方式一共有 256
种,也就是 2 ** 8
12.3.2 UTF-8
编码格式
计算机中使用 1~6 个字节 来表示一个 UTF-8
字符,涵盖了 地球上几乎所有地区的文字
大多数汉字会使用 3 个字节 表示
12.3.3 Ptyhon 2.x 中如何使用中文
在 Python 2.x 文件的 第一行 增加以下代码,解释器会以 utf-8
编码来处理 python 文件
# *-* coding:utf8 *-*
或者
# coding=utf8
在 Python 2.x
中,即使指定了文件使用 UTF-8
的编码格式,但是在遍历字符串时,仍然会 以字节为单位遍历 字符串
要能够 正确的遍历字符串,在定义字符串时,需要 在字符串的引号前,增加一个小写字母 u
,告诉解释器这是一个 unicode
字符串
# *-* coding:utf8 *-*
# 在字符串前,增加一个 `u` 表示这个字符串是一个 utf8 字符串
hello_str = u"你好世界"
print(hello_str)
for c in hello_str:
print(c)
13.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
计算器
input_str = input("请输入一个算术题:")
print(eval(input_str))
在开发时千万不要使用
eval
直接转换input
的结果
__import__('os').system('ls')
等价代码
import os
os.system("终端命令")
- 执行成功,返回 0
- 执行失败,返回错误信息