Python¶
版本管理¶
Python 语言和软件包更新频繁,不同的项目会使用不同版本的语言和依赖,因此需要做版本控制。
从 Python 3.3 开始,一般使用 Python 自带的模块 venv 来创建虚拟环境:
创建虚拟环境并加载环境变量后,就能使用 pip 命令来安装依赖包了。
需要注意的是 venv 配置文件使用绝对路径,因此环境不可移植(跨系统、跨设备)。最好的方式是使用 requirements.txt 文件来记录依赖包,这样可以保证不同系统下的依赖包一致。
Python 书籍目录¶
我的 Python 学习之路从经典的 Python 三剑客丛书开始。
| 中文名 | 英文名 |
|---|---|
| Python 程序设计基础(原书第 4 版) | Starting Out with Python, 4th edition |
| Python 编程:从入门到实践 | Python Crash Course: A Hands-On, Project-Based Introduction to Programming |
| Python 编程快速上手:让繁琐工作自动化 | Automate the Boring Stuff with Python |
| Python 数据科学手册 | Python Data Science Handbook |
一些书评:
- 《Python 编程快速上手》:目的性很强,快速上手 Python 在实际应用中最常见的用法,只需要写用完就扔掉的代码。因此它并不会向你介绍很多编程方面的细节,甚至根本不提面向对象等知识。在我看来,这本书适合希望快速将 Python 应用到学习工作中的读者。如果选择 SOP 和 PCC 等书,那么大部分的时间都将花在编程和语言的学习上,并不能立刻将它应用到工作上。
基础语法¶
关键字和内置函数
- 定义:
def、class等 - 布尔等表达式运算:
False、True、None、and、or、not等 - 控制结构:
while、for、if、else、elif等 - 其他
if 语句¶
-
比较方法:
in检查特定值是否在列表中- 使用
.lower()检查字符串是否为小写(注意个这个函数并不能直接检测,怎么用呢?)
-
if-elif-else结构 - 一些布尔表达式:
- Python 中支持连续的比较,如
age >= 18 and age <= 65可以简写为18 <= age <= 65。
- Python 中支持连续的比较,如
循环¶
- 使用
break和continue - 设置标志
- 用循环处理列表和字典
- 删除指定值
- 移动元素
-
学习下面这种用法,以列表作为循环条件:
函数¶
参数¶
- 关键字实参:
function_name(parameter=value)' - 默认值:
def function_name(parameter=value)- 在函数定义中,应当将没有默认值的参数放在前面,有默认值的参数放在后面,以便 Python 依然能够正确解读位置实参。
- 禁止函数修改列表:
function_name(list_name[:])(创建副本) - 任意数量实参:
def function_name(*parameters),*会创建一个空元组,将所有值都封装到这个元组中。 - 任意数量的关键字实参:
def function_name(**parameters),**会创建一个空字典,将所有值都封装到这个字典中。
参数的顺序
- 在定义和调用中,都应当按普通形参、带默认值的形参、任意数量形参的顺序排列。
将函数存储在模块中¶
- 导入整个模块:
import module_name - 导入特定函数:
from module_name import function_name - 函数别名:
from module_name import function_name as fn - 模块别名:
import module_name as mn - 导入所有函数:
from module_name import *
函数代码规范
- 形参指定默认值时等号两边不要有空格
- 函数调用时,关键字实参等号两边不要有空格
- 对齐参数列表行
类¶
编写类的基础¶
- 与类有关的每个方法都需要
self参数,且必须位于参数列表的第一个位置。 - 类的每个属性都必须有初始值。
继承¶
class ChildClass(ParentClass):
"""docstring"""
def __init__(self, parameters):
super().__init__(parameters)
self.attribute = value
- 子类的方法
__init__()需要父类的方法__init__()来初始化父类的属性。 super()是一个特殊函数,帮助 Python 将父类和子类关联起来。super().__init__(parameters):调用父类的方法__init__()。- 重写父类的方法
- 将实例用作属性(其实就是类的嵌套):一些类的细节越来越多,应当拆分成多个协同工作的小类。
异常¶
try-except-else代码块pass语句:什么都不做,只是占位符,使代码结构正确。
代码风格¶
- 类名使用驼峰命名法
- 实例名和模块名使用小写字母和下划线
- 每个类定义后都应当包含一个文档字符串
- 每个模块都应包含一个文档字符串
字符串与 I/O¶
字符串的写法:
r前缀:原始字符串,不转义。常用于路径、正则表达式。- 三重引号:其间的所有引号、制表符或换行都被认为是字符串的一部分。Python 的代码缩进规则不适用于三重引号,你的缩进也会被打印出来。三重引号推荐这样写,保证输出与代码看起来一致。其中第一行的
\防止文段开头多一个换行符。
print('''\
Dear Alice,
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Sincerely,
Bob''')
- 多行字符串还经常用作多行注释。
各类字符串方法:
isX()系列:isalpha()、isalnum()、isdecimal()、isspace()、istitle()。- 转换:
upper()、lower()、title()、capitalize()、swapcase()。 - 查找:
startswith()、endswith()。 - 多字符串:
join()、split() - 裁剪:
strip()、lstrip()、rstrip()。 - 对齐:
ljust()、rjust()、center()。 -
将其他数据转换为字符串
str(value) -
修改大小写
.title()、.upper()、.lower() - 拼接
-
删除空白
.rstrip()、.lstrip()、.strip()空白
空白指任何非打印字符。
文件¶
with语句:在不再需要访问文件后将其关闭。
with open(filename) as file_object:
contents = file_object.read() # 读取整个文件
for line in file_object: # 逐行读取
print(line.rstrip())
open():默认以只读模式打开文件。
考考你
以下问题如何解决?
- 读取文件时,空行会出现两次换行
- 将文件内容全部合并成一行
- 从文件中读取数字等其他类型的值
文件对象操作:
.read():读取整个文件.write():写入文件。注意不会在末尾添加换行符。
剪贴板¶
使用 pyperclip 包(需要安装)可以与系统剪贴板交互。
pyperclip.copy():将字符串复制到剪贴板。pyperclip.paste():将剪贴板中的内容粘贴到字符串。
练习
从剪贴板上获取多行字符串,将它们转换成 Markdown 格式的列表。
import pyperclip
text = pyperclip.paste()
lines = text.split('\n')
for i in range(len(lines)):
lines[i] = '* ' + lines[i]
text = '\n'.join(lines)
pyperclip.copy(text)
关注代码中如何处理换行符。
正则表达式¶
re 模块处理正则表达式:
re.compile():创建一个正则表达式对象(Regex 模式对象)。- 用例:
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') - 记得用原始字符串。
- 用例:
- Regex 对象有如下方法:
search():找第一次匹配- 输入:一个字符串。
- 返回:一个 Match 对象,包含匹配文本的第一个出现的位置。
- Match 对象有如下方法:
group():返回匹配的字符串。- 输入:一个整数(表示分组),0 或留空表示整个匹配。
- 返回:对应的字符串。
findall():找所有匹配- 输入:一个字符串。
- 返回:一个字符串列表,包含匹配的所有字符串。
- 如果有分组,返回的是元组的列表。每个元组是一个匹配,每个元组中的项是改匹配中的各个分组。
sub():替换操作- 输入:两个参数,第一个是用于取代匹配的字符串,第二个是要被替换的字符串。
- 在第一个参数中,可以使用
\1、\2等来引用匹配的分组。
- 在第一个参数中,可以使用
- 返回:替换后的字符串。
- 输入:两个参数,第一个是用于取代匹配的字符串,第二个是要被替换的字符串。
正则表达式语法
()分组:- 分组就是将多个字符当作一个单元。
|管道:匹配多个表达式中的一个。?可选匹配:前面的分组出现 0 次或 1 次。*零次或多次匹配:前面的分组出现 0 次或多次。+一次或多次匹配:前面的分组出现 1 次或多次。{n}匹配 n 次:前面的分组出现 n 次。- 可以指定一个范围,比如
{1,3}。第一或第二个数字可以不写。
- 可以指定一个范围,比如
?非贪心匹配:限制前面的分组匹配尽可能少的文本。- 默认情况下,正则表达式是贪心的,即匹配尽可能长的字符串。
字符分类:
\d任何数字\D除数字外的任何字符\w任何字母、数字或下划线字符(可以认为是匹配“单词”字符)\W除字母、数字和下划线以外的任何字符\s空格、制表符或换行符(可以认为是匹配“空白”字符)\S
使用中括号创建自己的字符分类。
^:匹配必须发生在字符串开始处。$:匹配必须发生在字符串结尾处。.:通配符,匹配除换行符外任何字符。- 如果要匹配换行符,使用
re.DOTALL作为re.compile()的第二个参数。
- 如果要匹配换行符,使用
- 大小写无关匹配:使用
re.IGNORECASE或re.I作为re.compile()的第二个参数。
管理复杂的正则表达式:
- 使用
re.VERBOSE或re.X作为re.compile()的第二个参数,可以在正则表达式中添加注释。
用例:
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))? # area code
(\s|-|\.)? # separator
\d{3} # first 3 digits
(\s|-|\.) # separator
\d{4} # last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? # extension
)''', re.VERBOSE)
加了注释,正则表达式就清晰多啦!
使用管道符号可以将多个 re.compile() 的第二个参数结合起来使用,比如 re.compile(r'pattern', re.IGNORECASE | re.DOTALL)。
数据结构¶
列表¶
基础:
- 添加元素:
.append(value)、.insert(index, value) - 删除语句:
del list[index]、.pop(index)(尾部删除)、.remove(value) - 排序:
.sort()、.sort(reverse=True) - 反转:
.reverse() - 长度:
len(list)
更多操作:
- 遍历
循环结束后
for 循环结束后,迭代使用的变量仍然存在。
range(start, end, step):生成一个整数序列,不包含end,默认start=0,step=1。- 切片
list[start:end:step]:不包含end,默认start=0,step=1。- 利用切片复制列表
list[:]。
- 利用切片复制列表
列表解析
这是一种比较高阶但常用的技巧,使用一行代码生成特定的列表:
- 首先指定列表名
- 再定义一个表达式
value**2生成你要存储到列表中的值 - 接下来写一个
for循环给表达式提供值
请练习生成一个 3 的倍数的列表。
字典¶
基础:
- 访问:
dict['key'] - 添加:
dict['key'] = 'value' - 创建空字典:
dict = {} - 修改值:
dict['key'] = 'new value' - 删除键值对:
del dict['key']
较长字典的缩进方法:
- 遍历:
for key, value in dict.items():for key in dict.keys():for value in dict.values():- 按顺序遍历
for key in sorted(dict.keys()):
更多操作:
- 字典的列表
- 字典中嵌套列表
- 字典嵌套字典
大型项目¶
Modules¶
Module:
- Python 文件就是 Module,文件名和模块名一致,模块内部
__name__变量为模块名,例如numpy.__name__=numpy -
导入模块:
解释器会搜索
sys.builtin_module_names和sys.path的路径,包括:- 输入脚本的文件夹
PYTHONPATHsite模块提供的安装路径,如site-packages
-
执行模块:此时
__name__为__main__ - 为了加快载入,Python 会将模块编译为
__pycache__/module.version.pyc,这是一种字节码 sys标准模块内置在每个 Python 解释器中-
dir()可以显示模块定义的所有名字(变量、函数、模块),无参数时显示当前定义的所有名字,内置的名字不自动显示
Package:
-
__init__.py让解释器认为该文件夹是一个 Package-
可以定义
__all__,当from a import *时将导入其中包含的模块如果不定义,则不会导入子模块
-
-
在包内可以使用相对(没有斜杠)和绝对路径导入其他模块
找不到 module?
去 PATH 中找,找不到就看安装过程,是否把源码装进去了。比如 pyproject.toml 中:
才会安装源码,否则可能只安装二进制。
Python Packaging User Guide¶
| 术语 | 含义 |
|---|---|
| Pure Module | 单个 Python 文件构成的模块 |
| Extension Module | 由其他语言编写的 Python 扩展,通常是动态库文件(如 .so) |
| Import Package | 可以包含子模块或其他包的 Python 模块 |
| Distribution Package / Project | 可以安装的软件,可以提供多个 Import Package 例:Pillow 提供 PIL |
- 源码分发
- 任何包含 Python 文件的目录都被视为一个 Import Package
- 如果是纯 Python 编写,就可以使用源码分发
- 称为 sdist,为
.tar.gz格式,包含一个或多个 Package 或 Module - sdist 规范要点:命名为
{name}-{version},包含pyproject.toml和PKG-INFO文件
- 二进制分发
- 如果依赖非 Python 库,应当使用二进制分发
- 使用 Wheel 格式打包二进制,并且 pip 偏好这种格式,即使存在源码包,因为它更快
- Whell 格式要点:ZIP 格式压缩,扩展名
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl- 安装过程:解压、移动到指定目录、编译所有
.py为.pyc
- 安装过程:解压、移动到指定目录、编译所有
- 此外还有一种老的 egg 格式,已经被弃用
打包流程:
-
准备源码树和
pyproject.toml,它指定了构建工具这里称为 backend,是因为由 frontend 来运行它们。frontend 可以是 pip、build 等工具。
-
构建 sdist 和一些 wheel
-
上传到 PyPI 等分发服务
setuptools¶
setup.py 是已经弃用的方式,应当改用 pyproject.toml。
setuptools 作为后端,不需要手动下载,用上面的 build 前端可以自动下载调用。
Pybind11¶
测试¶
unittest 模块:用于核实函数的行为是否符合预期。
- 导入
unittest模块和要测试的函数 - 创建一个继承
unittest.TestCase的类
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase):
"""测试 name_function.py"""
def test_first_last_name(self):
"""能够正确地处理像 Janis Joplin 这样的姓名吗? """
formatted_name = get_formatted_name('janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin')
unittest.main()
NamesTestCase中所有以test_开头的方法都将自动运行。- 常用的断言方法:
assertEqual(a, b):核实 a == bassertNotEqual(a, b):核实 a != bassertTrue(x):核实 x 为 TrueassertFalse(x):核实 x 为 FalseassertIn(item, list):核实 item 在 list 中assertNotIn(item, list):核实 item 不在 list 中
setUp()方法:只需创建一次对象,然后在每个测试方法中使用它。- 测试结果:
.:测试通过E:测试引发错误F:测试断言失败
标准库¶
collections:包含很多有用的类OrderedDict类:记录键值对的添加顺序
-
argparseparser = argparse.ArgumentParser() parser.add_argument("--input", ...) args = parser.parse_args() print(f"{args.input}")- 以
-开头的识别为可选参数,否则为位置参数 -
参数最终存放的变量名由
add_argument()的dest参数决定,默认是对第一个长选项名进行 strip:移除开头的-,将中间的-转换为_
- 以
常用第三方库¶
¶
待整理
Projects¶
Project 2 数据可视化¶
json 模块¶
在第 10 章我们学习了 json 模块,现在简单回顾复习文件操作和模块使用:
json.dump(data, file_object):将 Python 数据结构转换为 JSON 格式并写入文件。json.load(file_object):从文件中读取 JSON 格式的数据并转换为 Python 数据结构。
示例:
import json
filename = 'username.json'
try:
with open(filename) as file_object:
username = json.load(file_object)
except FileNotFoundError:
username = input("What's your name? ")
with open(filename, 'w') as file_object:
json.dump(username, file_object)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
matplotlib¶
查阅手册
需要时请翻阅手册查找用法
以下功能应该如何实现?
- 绘制:折线图、散点图
- 设置样式:
- 文字:标题、坐标轴标题
- 刻度(
tick_params):标记、大小、颜色 - 线条:粗细、颜色
- 点:大小、颜色
- 各部分的字体大小
生成数据¶
完成以下任务:
- 绘制从 1 到 100 的平方图像(列表解析)。
- 用随机漫步方法绘制图像,可以使用散点图和折线图(
random.choice())。 - 为图表使用颜色映射。
- 使用 Pygal 生成矢量图形文件。
- 写一个掷骰子实验,一个和多个骰子。
CSV 文件¶
csv模块:用于读取和写入 CSV 文件。csv.reader(file_object):读取文件并返回一个读取器对象,其中包含以逗号分隔的值。next(reader):返回文件中的下一行。
其他函数:
enumerate():返回一个包含索引和值的元组列表。datetime模块:strptime():将字符串转换为日期对象。
零散 tips¶
None值:这是NoneType数据类型的唯一值。如果你希望变量中的值不会和其他东西混淆,你可以使用它。- 使用没有返回值的函数进行赋值也会得到
None,你可以认为 Python 在这些函数末尾都加上了return None。
- 使用没有返回值的函数进行赋值也会得到
- 引用到底是怎么回事?
- 你可以将变量看作一个包含值的盒子
- 对于不可变数据类型的值,比如字符串、整型或元组,盒子里装的就是值本身。
- 对于可变数据类型的值,比如列表、字典,盒子里装的是值的引用。
- 技巧:
copy模块:copy.copy():复制列表或字典这样的可变值。但是,如果列表的里面有列表呢?copy.deepcopy():同时复制列表、字典或列表中的列表这样的可变值。
- 技巧:
pprint模块 漂亮打印:pprint.pprint():将列表、字典等数据类型打印成漂亮的格式。- 如果希望得到字符串,使用
pprint.pformat()。