How to write a Python package (从零开始做一个 Python 软件)

2022-01-21 10:03:35 +08:00
 SuperMild

我以前曾学过 Python, 但用得不算多, 后来有几年完全没有使用 Python, 现在已经忘得差不多了. 趁着这次重新使用 Python, 顺便把这个 "复习+学习" 的过程记录下来, 也许以后自己回头看也有用.

1. 变量的作用域非常重要

因此, 复习的时候, 我首先就找变量作用域的教程来看, 主要搞懂 nonlocal 和 global 就可以了, 不需要研究得太深, 遇到问题再研究.

2. 如何引用模块

如何引用 (import), 以及怎样组织代码 (packages and sub-packages), 也是了解个大概就可以了.

3. 类(class)

受到 Go 的影响 (最近几年我用 Go 比较多), 我会尽量不去使用 "继承" 的特性, 但 class 对于更有条理地组织代码是很有帮助的.

同样是 Go 的影响, 我寻找类似 interface 的东西, 找到了 Abstract Base Classes, 另外再了解 classmethod 与 staticmethod 这两个重要概念, 我认为暂时就够用了.

4. 库(libraries)

这次我用 Python, 是想写一个可以轻松写插件的文件 /文件夹操作工具, 是个纯命令行程序, 并且用 YAML 或 TOML 来方便用户输入参数.

在实际动手写程序之前, 我习惯先找一下相关的库, 确定项目的可行性, 如果找不到相关的库, 或者看起来很不好用, 我就要考虑换语言了.

结果找到 importlib, Click, tomli 等, 看起来都很不错.

5. 为发布软件做准备

以前我写程序不考虑发布出去给别人用, 但后来 GitHub 流行了, 恰巧用 Go 写的软件只需要发布在 GitHub 即可, 我就顺便发布了. 因此现在写程序已经习惯了考虑如何发布.

Python 程序的发布中心是 pypi.org, 为了避免用户名或程序名与别人冲突, 到时写好程序才发现可能就要改名, 因此我先去 pypi 注册.

6. 正确的目录结构

发布到 pypi.org 的程序必须有正确的目录结构, 以及一些配置文件 (比如项目名称, 版本号等), 在这里有详细说明 https://packaging.python.org/tutorials/packaging-projects/

另外找到一个帮助打包的好东西 https://github.com/pypa/flit

7. 虚拟环境 (virtualenv)

对于 Python 来说,虚拟环境非常重要,因此 Python 里有很多工具帮助解决这个问题,官方也提供了工具。一般来说,官方的工具就够用了,详见官方文档 https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/

如果需要更丰富的功能,推荐使用 minicondapyenv

8. 生成可执行命令

有的 Python 程序当作一个库来使用,那就正常写程序就行,而如果要当作一个命令行程序来运行,可采用这种方法 https://click.palletsprojects.com/en/8.0.x/setuptools/#setuptools-integration

采用这个方法时要注意两点:

采用上述方法后,可在项目根目录用命令 pip install --editable .flit install 进行本地安装,只需要本地安装一次,修改代码无需再次安装。

9. 格式化, 语法检查

还是受到 Go 的影响, Go 从一开始就强调代码格式化的重要性并且官方自带 formatter (当然, 只是说我受到 Go 的影响, 事实上各种语言都很重视这方面的工具), 很高兴在 Python 这边也找到了优秀的工具: Black, Flake8, Mypy, Pylance

类型检查

其实我写 Python 并不是太刻意追求类型安全,我决定用 type hints 最主要的目的是可以少写文档、更清晰地写文档。注明参数的类型时,用人类语言太麻烦,还不如用 type hints 更简明。

Mypy 容易安装容易使用,但默认行为不够严格,我没去研究如何提高 mypy 的严格程度,改用 Pylance, 在 VS Code 里设置 type checking mode 从 off 改为 basic 后发现严格程度刚刚好(如果改为 strict 则太严格),因此就这样用了。

10. 开始编写程序

到了正式开始编程,倒没什么特别要说的了,源代码的更新记录包含了一切细节。

ffe 是我第一次发布到 pypi 的软件,主要用途是管理自己日常写的零散脚本。最近升级到版本 v0.1.1, 新增了 use_pipe 参数方便多个任务串联。

使用命令 ffe dump -r mimi 可以获得插件 mimi 的 toml 文件:

[[tasks]]
recipe = "mimi"
names = []

[tasks.options]
suffix = ".mimi"
overwrite = false

采用同样的方法获得别的插件的 toml 文件,然后复制黏贴到同一个文件里,例如:

[[tasks]]
recipe = "mimi"    # 第一个任务:加密
names = [
  'file.txt',
]

[tasks.options]
suffix = ".mimi"
overwrite = false

[[tasks]]
recipe = "anon"    # 第二个任务:匿名上传
names = []   # 第二个任务既可指定具体文件名,也可接受上一个任务的结果

[tasks.options]
auto_copy = true
key = ""
use_pipe = true   # 设为 true 表示接受上一个任务的结果

然后使用命令 ffe run -f mimi-anon.toml 即可依次执行任务。第一个任务加密后会生成文件 file.txt.mimi, 并把这个文件名传给第二个任务。

如果想使用同一个流程来处理另一个文件,不需要任何改动,只要使用命令 ffe run -f mimi-anon.toml file2.txt 即可对 file2.txt 进行加密和上传。

如果有一些文件需要经常加密上传,这个任务组合就很方便了。还可以把打包压缩、删除文件等任务都添加进去,这比 GUI 工具更灵活,编辑 TOML 文件也很直观。

(关于我写的加密插件 mimi 的安全性在这里 https://v2ex.com/t/827768 有很充分的讨论)
(关于匿名上传的插件的说明详见这里 https://github.com/ahui2016/ffe/blob/main/docs/anon-ibm.md

2650 次点击
所在节点    Python
5 条回复
qyd0801
2022-01-21 10:29:44 +08:00
还以为是英文贴
SuperMild
2022-01-21 10:31:50 +08:00
@qyd0801 我考虑不周,现在想改标题了…
meiyoumingzi6
2022-01-21 12:22:19 +08:00
看到 python 跟 GO 的对比,
个人看法是, 不要用一个语言套用在另一个语言上
1. 作用域. 能少用 global 跟 nonlocal 就尽量少用, 在 go 里面 global 变量是挺多的
3. class, 继承用起来还是很舒服的, 菱形继承[python3]理解清楚后, 继承用起来很爽的, self 也会是当前实例, go 里面的话即使内嵌接受者还是原来的类型
4. yaml/toml 用起来不错, 当然也可以直接用 py 文件作为配置, 用起来也会更加舒服
rpman
2022-01-21 14:03:41 +08:00
我突然觉得是不是现在的人都不用学 c++了
SuperMild
2022-01-21 15:56:42 +08:00
@meiyoumingzi6 感谢指教,看了你说的,我才发现我的帖子有个事情没说清楚。

我这个主要是假设有其他语言经验的人,想用 Python 快速开始做个东西,目的是先把东西做出来,后续再慢慢转换为 Python 的思想。

yaml/toml 有个大优点,就是可以减少字符转义,比如 Windows 文件目录里有反斜杠,用 yaml/toml 可以直接写,比较直观。另外就是我除了要读取内容,还要生成 toml 文件,这方面生成 py 文件就不好办了。


@rpman 我是从 C 入门的,也想过学学 C++,但一直拖着,因为太庞大了,看着有点害怕。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/829623

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX