Python进阶
本部分侧重于介绍常用的,与开发息息相关的Python标准库。
Python 标准库非常庞大,所提供的组件涉及范围十分广泛,正如以下内容目录所显示的。这个库包含了多个内置模块 (以 C 编写),Python 程序员必须依靠它们来实现系统级功能,例如文件 I/O,此外还有大量以 Python 编写的模块,提供了日常编程中许多问题的标准解决方案。其中有些模块经过专门设计,通过将特定平台功能抽象化为平台中立的 API 来鼓励和加强 Python 程序的可移植性。
Windows 版本的 Python 安装程序通常包含整个标准库,往往还包含许多额外组件。对于类 Unix 操作系统,Python 通常会分成一系列的软件包,因此可能需要使用操作系统所提供的包管理工具来获取部分或全部可选组件。
数据类型
👍enum 模块
基本示例
from enum import Enum
class TrafficLight(Enum):
RED = 1
YELLOW = 2
GREEN = 3
# YELLOW = 1 #常见错误:将枚举成员的值设置为相同类型
# RED = [255, 0, 0] # 常见错误:错误地将枚举成员的值设置为可变类型
# YELLOW = None # 常见错误:错误地将枚举成员的值设置为None、False、True等
# 使用枚举
light = TrafficLight.RED
# if light == 1 常见错误:错误地使用Enum成员进行比较
if light == TrafficLight.RED:
print("红灯,停车")
elif light == TrafficLight.YELLOW:
print("黄灯,准备")
else:
print("绿灯,通行")
是否使用Enum
取决于你项目的需求和代码的复杂度。你可能觉得不需要它,尤其是在简单的场景下,直接使用字符串或整数常量看似足够。但是,Enum
有以下几个优势:
1. 增加可读性
当你看到Color.RED
时,比直接看到一个1
或'red'
更容易理解。Enum
能让你的代码更具语义化,避免硬编码的常量值。
例如:
status = Status.SUCCESS # 一目了然:状态是成功
相比:
status = 1 # 需要额外判断 1 是什么意义
2. 减少错误
使用Enum
可以避免常见的错误,比如不小心使用了错误的值或者字符串拼写错误。Enum
成员是唯一的,且不可变的,能够防止无意间改变它们的值。
比如,如果你用了Status.SUCCESS = 1
,然后在后续代码中某处错误地设置了Status.SUCCESS = 2
,你会收到警告或报错,而不是默默覆盖,产生潜在的 bug。
3. 类型安全
使用Enum
可以确保变量的值只来自于枚举成员,而不会误用其他类型的值(如普通的数字、字符串等)。这对于大型项目来说尤其重要,因为它能有效地避免一些奇怪的 bug。
比如:
def set_color(color: Color):
if not isinstance(color, Color):
raise ValueError("Invalid color")
4. 易于扩展和维护
随着项目的扩展,你可能会有更多的常量值需要添加,Enum
让这种扩展变得更清晰、更系统化。你不再需要在多个地方定义相同的常量,所有的常量都集中在一个地方。
比如,随着系统需求变化,你可能需要扩展交通灯的状态:
class TrafficLight(Enum):
RED = 1
YELLOW = 2
GREEN = 3
FLASHING = 4 # 新增状态
5. 集成与协作的优势
在多人开发的团队中,使用Enum
可以提高协作性。它使得每个成员的代码中常量的含义更加清晰,减少误解或重复定义的问题。
6. 可迭代、可比较
Enum
支持迭代、比较等操作,允许你灵活处理。例如,你可以遍历所有的Enum
成员,或者比较它们的顺序。
for state in TrafficLight:
print(state)
文件
os 模块
库、包、模块的包含关系为:多个模块组成为包、多个包组成为库。
在实际开发中不做严格区分。
Python 标准库:Python 内置的库,不需要安装,直接导入即可使用。
以 Python 的内置 os 模块为例,是与操作系统进行交互的模块,主要有如下功能:
文件路径操作
- os.remove(path) 或 os.unlink(path) :删除指定路径 的文件。路径可以是全名,也可以是当前工作目录下的路径。
- os.removedirs:删除文件,并删除中间路径中的空文件夹
- os.chdir(path):将当前工作目录改变为指定的路径
- os.getcwd():返回当前的工作目录
- os.curdir:表示当前目录的符号
- os.rename(old, new):重命名文件
- os.renames(old, new):重命名文件,如果中间路径的文件夹不存在,则创建文件夹
- os.listdir(path):返回给定目录下的所有文件夹和文件名,不包括 '.' 和 '..' 以及子文件夹下的目录。('.' 和 '..' 分别指当前目录和父目录)
- os.mkdir(name):产生新文件夹
- os.makedirs(name):产生新文件夹,如果中间路径的文件夹不存在,则创建文件夹
导入该模块:
import os
产生文件:
f = open('test.file', 'w')
f.close()
print('test.file' in os.listdir(os.curdir))
重命名文件:
os.rename("test.file", "test.new.file")
print("test.file" in os.listdir(os.curdir))
print("test.new.file" in os.listdir(os.curdir))
# 删除文件
os.remove("test.new.file")
系统常量
- windows 为 \r\n
- unix 为 \n
os.linesep
# 当前操作系统的路径分隔符:
os.sep
当前操作系统的环境变量中的分隔符(';' 或 ':'):
- windows 为 ;
- unix 为:
os.pathsep
os.environ 是一个存储所有环境变量的值的字典,可以修改。
os.environ
os.path 模块
import os.path
- os.path.isfile(path) :检测一个路 径是否为普通文件
- os.path.isdir(path):检测一个路径是否为文件夹
- os.path.exists(path):检测路径是否存在
- os.path.isabs(path):检测路径是否为绝对路径
windows 系统:
print(os.path.isfile("C:/Windows"))
print(os.path.isdir("C:/Windows"))
print(os.path.exists("C:/Windows"))
print(os.path.isabs("C:/Windows"))
unix 系统:
print(os.path.isfile("/Users"))
print(os.path.isdir("/Users"))
print(os.path.exists("/Users"))
print(os.path.isabs("/Users"))
split 和 join
- os.path.split(path):拆分一个路径为 (head, tail) 两部分
- os.path.join(a, *p):使用系统的路径分隔符,将各个部分合成一个路径
head, tail = os.path.split("c:/tem/b.txt")
print(head, tail)
a = "c:/tem"
b = "b.txt"
os.path.join(a, b)
def get_files(dir_path):
'''
列出文件夹下的所有文件
:param dir_path: 父文件夹路径
:return:
'''
for parent, dirname, filenames in os.walk(dir_path):
for filename in filenames:
print("parent is:", parent)
print("filename is:", filename)
print("full name of the file is:", os.path.join(parent, filename))
列出当前文件夹的所有文件:
dir = os.curdir
get_files(dir)
Byte Code 编译
Python, Java 等语言先将代码编译为 byte code(不是机器码),然后再处理:
.py -> .pyc -> interpreter
eval(statement, glob, local)
使用 eval 函数动态执行代码,返回执行的值。
exec(statement, glob, local)
使用 exec 可以添加修改原有的变量:
a = 1
exec('b = a + 10')
print(b)
local = dict(a=2)
glob = {}
exec("b = a+1", glob, local)
print(local)
compile 函数生成 byte code: compile(str, filename, mode)
a = 1
b = compile('a+2', '', 'eval')
print(eval(b))
a = 1
c = compile("b=a+4", "", 'exec')
exec(c)
print(b)
# abstract syntax trees
import ast
tree = ast.parse('a+10', '', 'eval')
ast.dump(tree)
a = 1
c = compile(tree, '', 'eval')
d = eval(c)
print(d)
# 安全的使用方法 literal_eval ,只支持基本值的操作:
b = ast.literal_eval('[10.0, 2, True, "foo"]')
print(b)
open
写文件
我们使用 open 函数的写入模式来写文件:
f = open('test.txt', 'w')
f.write('hello world.')
f.close()
print(open('test.txt').read())
使用 w 模式时,如果文件不存在会被创建
除了写入模式,还有追加模式 a
读写模式 w+
f = open('test.txt', 'w+')
f.write('hello world. morning.')
f.seek(3)
print(f.read()) # hello world.
f.close()
读文件
使用 open 函数 来读文件,使用文件名的字符串作为输入参数:
默认打开文件是 ‘r’ 读模式
f = open("test.txt")
# 默认以读的方式打开文件,如果文件不存在会报错。
# 可以使用 read 方法来读入文件中的所有内容:
text = f.read()
print(text)
按照行读入内容,readlines 方法返回一个列表,每个元素代表文件中每一行的内容:
f = open("test.txt")
lines = f.readlines()
print(lines)
f.close()
# 事实上,我们可以将 f 放在一个循环中,得到它每一行的内容:
f = open('test.txt')
for line in f:
print(line)
f.close()
上下文管理器
with open('my_file.txt', 'w') as fp:
data = fp.write("Hello world")
这等效于下面的代码,但是要更简便:
fp = open('my_file.txt', 'w')
try:
# do stuff with f
data = fp.write("Hello world")
finally:
fp.close()