Python 两则
Python 程序分发,你还在用 conda 煎药熬汤吗?你还在用自己写的安装脚本小钢磨打粉吗?Python 程序,现在开始打成包。打成包,高效分发。
其实是我最近看了一些神经网络项目的代码,不得不说大佬们的程序都写得非常漂亮,但是一些大佬似乎不太会使用 Python 的包管理工具。他们往往会在 README 中花很大的篇幅写如何配置运行环境。但实际上现在的 Python 的包管理和依赖管理已经十分好用了,以下两则建议会帮助你快速地配置 Python 环境。
Python package
创建一个路径,在这个路径下创建一个空文件 __init__.py
,那么这个路径就会被认为是一个 Python 的 package,在这个路径下的 Python 文件就是这个 package 下包含的 module。
pack
├── __init__.py
└── mod.py
在这个路径的上一层打开 Python,就可以 import 这个 package,或者从这个 package 导入 module:
>>> import pack
>>> from pack import mod
__init__.py
就有点类似于 Node.js 的 index.js
的概念,当你直接 import 这个包时导入的其实是 __init__.py
模块。这时的 __init__.py
还是空的,你已经可以 from pack import mod
了,但是却无法通过 pack
访问到 mod
。要让直接 import pack
包含 mod
模块,只需要在 __init__.py
中导入这个模块:
from . import mod
.
的意思就是这个模块所在的包。类似地,如果你的包下面还包含了一个子包叫 subpack
,subpack
下还有个模块叫 mod2
,那么要在父包的一个模块中导入这个模块就要这样写:
from .subpack import mod2
可以发现包可以帮助 Python 程序提高内联性。除此之外包还带来了其他的好处。运行 Python 程序除了直接 python my_program.py
以外,还有一种把 .py
文件当作模块的运行方式,也就是 python -m my_program
。一个打成包的 Python 程序在安装后,用户可以直接通过这种方式运行程序,而无须关心 .py
文件的完整路径。
了解了 Python 的包后,就可以开始打包了。
Python packaging
不算太久远的早期的一个 Python 的包管理器叫做 easy_intsall
,现在已经很少用了。现在大家一般都会用 pip
安装 Python 包。但无论是 easy_install
还是 pip
其实都是 setuptools
的前端,我们的 Python 打包也从 setuptools
开始。
在包的父路径创建一个文件 setup.py
,在里面写入:
from setuptools import setup, find_packages
setup(name='pack',
version='0.1',
author='John Toolman',
author_email='john@example.org',
packages=find_packages(),
install_requires=['some_dependency==0.0.1'])
这样就描述好了一个可以安装的包,使用 python setup.py install
即可安装到系统或用户文件中,全局都可以使用。
GNU/Linux 下它将被安装在 ~/.local/lib/python3.x/site-packges/
中。如果是 root 用户,它将被安装在 /usr/local/lib/python3.x/site-packages/
中。
name
安装的包名,这个名字可以和上面的包名不同version
版本author
作者author_email
作者邮箱packages
一个 list,列出这个安装的包中包含的所有 package,比如上面一节中就包含了两个包['pack', 'pack.subpack']
。一般使用find_packages()
自动列出所有包install_requires
这个包的依赖,是个 list,格式与requirements.txt
相同
除此以外你还可以指定程序的命令行入口,这样 setuptools
会把你程序的命令行工具安装在 /usr/local/bin/
或 ~/.local/bin/
中。如果你有把这些目录添加到 $PATH
里,你的程序就可以像一个普通的命令行程序一样方便使用。
scripts
指定.py
文件作为入口setup( ... scripts=['scripts/my-entry'] ... )
entry_points
指定包中一个模块的一个函数作为入口setup( ... entry_points={ 'console_scripts': ['my-entry = pack.entry:main'] } ... )
Upload to PyPI
有了 setup.py
后,就可以打包成 wheel 并上传到 PyPI(亦或是自建的私有 PyPI,通过 --repository-url
指定)上了。
$: pip3 install --upgrade setuptools wheel twine
$: python3 setup.py sdist bdist_wheel
$: twine upload dist/*
现在你就可以通过 pip install
安装自己的包了。
Virtualenv
在 Telegram 聊天记录里搜索 Anaconda,大部分群组聊天记录都是这样的:
贵校的 Anaconda 镜像没了?
要啥 Anaconda
Python 3.8 太新了,需要 3.7
事实上,对于这种需要多种版本或者多个隔离环境的情况,PyPA 已经为我们提供了 virtualenv。假设你的系统上已经安装了个 Python 3.8,但你仍需要创建另一个独立的 Python 3.8 环境,那么你可以让 virtualenv 把系统上的 Python 拷贝过来一份:
$: virtualenv -p /usr/bin/python3.8 venv3.8
然后启用:
$: . venv3.8/bin/activate
(如果你使用的是 Windows,请使用 PowerShell 并且你需要 source 的是 venv3.8/bin/activate.ps1
)
这样就在当前 shell 中启用该 virtualenv 了,启动 python
时会启动 venv3.8
中的 Python,使用 pip 安装包也会装到 venv3.8
中去。如果你安装了 oh-my-zsh 之类的工具,你的 shell 的 prompt 上还会显示出你现在使用的 virtualenv。使用 deactivate
可退出,或者你也可以直接关掉这个 shell。不想要了直接把 venv3.8
路径删了就行了。
那如果系统上没有安装,源里也没有某个版本呢?
那我们可以下载源码,编译,然后从编译好的位置复制。
curl -LO https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xz
tar xf Python-3.6.0.tar.xz
cd Python-3.6.0
./configure
make
make DESTDIR=../python3.6.0-bin/ install
cd ..
virtualenv -p ./python3.6.0-bin/usr/local/bin/python3.6 venv3.6.0
. venv3.6.0/bin/activate