note/two-tips-o-python

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

. 的意思就是这个模块所在的包。类似地,如果你的包下面还包含了一个子包叫 subpacksubpack 下还有个模块叫 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='[email protected]',
      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

About Me