@Lenciel

Python in 2020 (1) - 环境搭建

目录

简介

最近两年做工具都在逼自己用刚学不久的 Go 和 Rust ,对 Python/Django 有些疏远了。最近想做一个 OKR 管理和对齐的工具,想着 Python 2 总算正式退役了, virtualenv 也改名 venv 成了标配,不如宠幸一把试试感觉。

结果光是包和依赖管理,就有 pip-tools/pyenv/Anaconda/pipenv/poetry/pipx/…,WTF,贵圈是被前端社区统治了吗…

大概看了一下各路的妖魔鬼怪,我觉得可以选一套自己还比较满意的工具链记录和分享一下。分为下面几个部分:

  1. 环境搭建
  2. 测试框架
  3. 静态检查
  4. 类型检查

环境搭建

我的需求:

  • 为各种不同项目管理和隔离它们的运行时环境(主要是 Python 版本)和依赖
  • 保持 Mac 系统本身的干净,包括自动的 Python 不被覆盖,全局生效的 zsh 配置等尽量不动
  • 部署项目的时候尽量使用 Docker ,但是在使用上它又慢又重又容易撞墙

配置

1. pyenv

选择的原因

pyenv 应该主要是借鉴了 nodenv,让你:

  • 可以安装多个 Python 版本然后根据需要在全局或者为每个项目配置
  • 可以和 tox 结合测试你的应用在不同 Python 版本下的运行情况
  • 可以结合 pyenv-virtualenv 对虚拟环境进行管理(我没有用,因为 Poetry 自带的对 venv 功能的封装足够用了)

安装

可以使用 brew 来安装 pyenv,但是我希望对它更可控所以选择直接:

$ git clone https://github.com/pyenv/pyenv.git 〜/.pyenv

因为我使用 zsh,所以在 ~/.zshrc 里面可以加上:

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
$ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.bash_profile

然后就可以安装不同版本的 Python 了:

$ pyenv install 3.8.1
$ pyenv install 3.7.2

查看有哪些可用版本然后配置全局优先使用 3.8.2 版本:

$ pyenv versions
  system
  3.7.2
  3.8.1
$ pyenv global 3.8.1

然后可以选择安装 pyenv-virtualenv,但是我打算用 poetry 来创建和激活虚拟环境,所以就没有安装。

2. Poetry

选择的原因

Poetry 使用了类似于前端生态里的 yarn 或者 bundler 的架构来做三件事情:

  • 管理依赖
  • 打包应用
  • 管理虚拟环境

如果你不想被 docker 拖垮可以试试这个。

安装

使用 curl 安装然后配置环境变量:

$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python

$ echo 'export PATH="$HOME/.poetry/bin:$PATH"' >> ~/.zshrc

安装后可以把生成虚拟环境的路径指定到项目的根目录:

$ poetry config virtualenvs.in-project true

新建一个测试项目:

$ poetry init --no-interaction

这会创建一个 pyproject.toml 文件:

[tool.poetry]
name = "test"
version = "0.1.0"
description = ""
authors = ["lenciel <lenciel@gmail.com>"]

[tool.poetry.dependencies]
python = "^3.8"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

然后你就可以用 TOML 语法来编辑这个文件了。

创建虚拟环境

virtualenv 被收编后改名为 venv,在我们声明了一个项目之后,通过下面的命令就会自动创建一个跟项目关联的虚拟环境:


$ poetry install
Creating virtualenv test in /Users/lenciel/Projects/engineering/test/.venv
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

No dependencies to install or update

在这个虚拟环境下面运行程序可以使用:

$ poetry run python test.py

另外,可以通过配置让虚拟环境的目录就生成在项目的根目录来方便查看:

$ poetry config virtualenvs.in-project true

管理依赖

可以通过 add 命令来添加依赖:

$ poetry add django

Using version ^3.0.8 for django

Updating dependencies
Resolving dependencies... (1.1s)

Writing lock file


Package operations: 4 installs, 0 updates, 0 removals

  - Installing asgiref (3.2.10)
  - Installing pytz (2020.1)
  - Installing sqlparse (0.3.1)
  - Installing django (3.0.8)

这个操作其实会:

  • 下载并安装所有的依赖包到虚拟环境
  • 安装好后依赖的详细信息会被注册到 poetry.lock 文件
  • 依赖的版本信息会被注册到 pyproject.toml

如果你熟悉前端的工具链这个就很像对 gem 的管理。包括 pyproject.toml 的依赖版本描述语法,比如 ^1.3.0 表示不低于 1.3.0 的版本。而 poetry.lock 文件里的版本则是具体被安装的版本,它可以用来保持整个团队的版本一致性,以及生产环境和开发环境的版本一致性

当需要更新某个依赖的时候,你即可以用 update 命令,也可以用类似 poetry add django^3.0.1 来指定更新到具体的版本。

3. pipx

选择的原因

pipx 和 pip 不一样的是它不仅仅是一个包管理工具,它会创建一个虚拟环境,然后让你很容易的运行某个制定的程序而不用担心影响到其他地方。基本上你可以把它理解成 pipsi 的续篇就好

安装使用

保障干净(YMMV)的安装方法:

$ python -m pip install pipx

基本使用

$ pipx install sphinx

inject 可以让你在 REPL 里面安装额外的包并直接 import:

$ pipx inject sphinx sphinx_rtd_theme

4. Docker

Docker 其实跟 Python 的环境搭建和依赖管理没有任何直接关系,但是大家经常在这种讨论中谈到它,因为容器技术的一大用途就是进行开发环境的搭建。

前面讨论的以及后面会讨论的所有东西,当然都可以安装到容器里面。而且说实话,你要为每个 Python 的应用建一个容器,那虚拟环境可能都不需要了。但我个人觉得,Docker 也有下面的缺点:

  • 比较重,从安装到使用到运行需要的资源
  • 调试起来需要大量的配置和对工具链额外的投入
  • 安装很多跟系统底层相关的依赖时比较麻烦
  • 实际上还是需要你至少使用 pip 等来管理依赖,换句话说,你只是在容器里面去执行前面说的那些命令而已

所以如果不是公司在容器化上有足够投入,并且你开发的应用也不涉及到 Python 以外的组件和依赖(例如,你开发的是 Django 的应用,还涉及 数据库,Nginx ,Gunicorn 等等别的依赖,Docker 可能就挺好用的),我是不推荐使用 Docker 的,可以做一个具体的对比:

  安装 Python 包 安装非 Python 包 管理多个 Python 版本 管理虚拟环境 环境重建便利
pip *1      
venv        
piptools        
pyenv        
Conda 2  
pipenv + pyenv
Poetry + pyenv
Docker         3

5. 不再使用的

下面两个是在之前工具链里面被直接去掉的:

  • virtualenv(Python 3.0 自带 venv)
  • pipsi (pipx 比它好用)

还有一个就是 virtualenvwrapper ,但我比较怀念在目录切换的时候自动 activate/deactivate 相应的虚拟环境,并且在 zsh 的 prompt 上有一个提示,这个可以用脚本:

#!/usr/bin/env zsh
ZSH_POETRY_AUTO_ACTIVATE=${ZSH_POETRY_AUTO_ACTIVATE:-1}
ZSH_POETRY_AUTO_DEACTIVATE=${ZSH_POETRY_AUTO_DEACTIVATE:-1}
ZSH_POETRY_OVERRIDE_SHELL=${ZSH_POETRY_OVERRIDE_SHELL:-1}

autoload -U add-zsh-hook

_zp_current_project=

_zp_check_poetry_venv() {
  local venv
  if [[ -z $VIRTUAL_ENV ]] && [[ -n "${_zp_current_project}" ]]; then
    _zp_current_project=
  fi
  if [[ -f pyproject.toml ]] \
      && [[ "${PWD}" != "${_zp_current_project}" ]]; then
    venv="$(command poetry debug 2>/dev/null | sed -n "s/Path:\ *\(.*\)/\1/p")"
    if [[ -d "$venv" ]] && [[ "$venv" != "$VIRTUAL_ENV" ]]; then
      source "$venv"/bin/activate || return $?
      _zp_current_project="${PWD}"
      return 0
    fi
  elif [[ -n $VIRTUAL_ENV ]] \
      && [[ -n $ZSH_POETRY_AUTO_DEACTIVATE ]] \
      && [[ "${PWD}" != "${_zp_current_project}" ]] \
      && [[ "${PWD}" != "${_zp_current_project}"/* ]]; then
    deactivate
    _zp_current_project=
    return $?
  fi
  return 1
}
add-zsh-hook chpwd _zp_check_poetry_venv

poetry-shell() {
  _zp_check_poetry_venv
}

if [[ -n $ZSH_POETRY_OVERRIDE_SHELL ]]; then
  poetry() {
    if [[ $1 == "shell" ]]; then
      _zp_check_poetry_venv || (
        echo 'pyproject.toml file not found' >&2;
        exit 1
      )
      return $?
    fi
    command poetry "$@"
  }
fi

[[ -n $ZSH_POETRY_AUTO_ACTIVATE ]] && _zp_check_poetry_venv

环境搭建就是这样,接下来是测试框架的选择和配置。

  1. pip 虽然搞不定,但是 pip wheels 可以安装大部分非 Python 的依赖。 

  2. Conda 并不能代替系统的包管理软件,如 yum 或者 apt-get,所以在不同的平台你可能需要做很多额外的工作。 

  3. 如前所述,Docker 对里面的 Python 怎么运行怎么进行包管理是无感的,所以你需要在 Docker 里面安装上面那些东西。 

你的赌注

目录

前言

最近在西瓜做了一次分享,其中有一个核心问题是如何面对生活和工作里的不确定性。里面有一些内容限于分享本身的目的,没有展开。所以干脆另开一篇,稍微讨论一下。

先做个实验。

花点时间回顾一下去年,你做的「最好的决定」和「最糟糕的决定」是什么?

准备好了吗?分享一下你的答案?

大部分人想出来的是带来「最好的结果」和「最差的结果」的决定。

在结果和决策质量之间建立错误的关联,只是人在面对不确定性时常犯的错误之一。

  • 如何在面对巨大的不确定性时少点儿不安?
  • 如何更好地区分运气和技能从而更好地决策?
  • 如何避免因为结果不好就埋怨自己的决策不佳?

可能首先需要充分认识和接受不确定性。

生活不是下象棋是打德扑

有人说现代社会是一个 VUCA 的时代。我觉得并不是现在才这样:这个世界一直是不可测的,甚至是不可知的。

打个比方的话,生活是打德扑,不是下象棋。

象棋没有隐藏信息,几乎也没有运气。水平高的人可以同时对弈 20 个人,还保持非常大的胜率。

德扑不是这样,有技巧的同时也有显然的运气成分:如果一局定胜负新手也可能打败世界冠军。

生活像德扑,你每天从办公楼闯红灯去马路对面吃午饭,坚持了十年,毫发无损;我在街口等灯被酒驾的人追尾,挂了。

即使做出最明智、最谨慎的决策,也可能得到一个凄惨的结果,这就是生活。

所以,生活的质量等于决策质量,加上运气。

我们都知道,这并不意味着你应该把生活交给运气,随波逐流:因为提高决策质量还是能有效地提高收益,降低风险。

但既然生活像德州,意味着你必须面对:

  • 永远不会有完整的信息。
  • 后续的发展有极大的随机性。
  • 自己和他人的经验中总结和吸收的东西常常没有用。

然后还得在有限的时间内进行决策。

怎么办?

理解你对抗的是什么

人天生的缺陷

人类在进化过程中形成的很多特性在今天未必对我们都是有利的。

比如因为饱一顿饿一顿,所以吃下超过所需的食物也不会觉得饱,于是在供给充分的现代社会,肥胖成了一个巨大问题。

面对不确定性时,人类的原生缺陷就更多了,比如人特别需要在混乱中得出秩序感:面对浩瀚的宇宙,多厄的命运,人类建立了数学这样的体系,也建立了神鬼、星象、八卦这样的体系,来对抗生活混乱的本质。

但,影响人更好地面对不确定性,最显著的缺陷是下面两个。

  1. 自利性偏差

自利性偏差是指,对自己的成功往往做个人归因,对失败做情境归因;而对别人的成功倾向于做情境归因,对失败做个人归因。

很多大平台的人觉得自己特别厉害,忽视了平台的力量。

车祸里 91% 的人觉得是对方全责。

对于同一场比赛,主队客队的球迷对裁判的尺度、对方的表现等各个方面的感受大不相同。

有兴趣的同学还可以去看看《Catching Hell》,讲自利性偏差如何让芝加哥小熊队的球迷毁掉了原本是他们一份子的 Steve Bartman 的生活。

总的来说,自利性偏差使得我们无法客观、开放地评估运气对我们或者他人的结果的影响,从而高估自己或者低估他人,无法更好地进行决策。

  1. 动机性推理

动机性推理是指,我们容易进入这样一种循环:因为自己的固有观念,选择性地去处理信息(无论是听到的看到的想到的),然后又用这些信息来强化我们的观念,并因此进一步按照同样的模式去处理信息。

这也是进化带来的。我们有理由假定我们的感官不是在说谎:如果你看到一棵树就在你面前,那么质疑这棵树是否存在通常就是在浪费认知能量。如果你听说草丛里有动静可能就会跳出一只老虎吃掉你,那么质疑这个推理是否成立的下场可能就是被吃掉。

所以,人们的观念形成其实是很随意的,远没有很多人自以为的那么科学。举例来说,很多人觉得狗的 1 岁相当于人的 7 岁,其实这根本不科学。

但人们对自己的观念的捍卫却是很固执的。

特别是互联网的出现,让我们拥有了前所未有多的信息,使得动机性推理变得前所未有的简单。只要稍微观察一下各种社交网络比如微信群,就可以看到人们是多么轻松就可以找到各种信息来证明自己的想法才是「对的」。

更糟糕的是,许多互联网产品开始定制化推送信息给用户:展示更多已经被确认是他们喜欢的东西。我从来不用头条的产品,仍然读书和 rss 就是我觉得这样的「信息流」只是一个「确定性的泡沫」。

总的来说,动机性推理使我们有意识地去捍卫自己固有的观念,于是押注到错误的决策上。

风险

当你正确地理解了什么是「不确定性」:世界是随机的,如果你只想要积极的结果,不要申请当宇航员,不要跟陌生人表白,不要购买盲盒,更不要创业。这个世界充满了让我们因为结果不好就难过地觉得自己做得不好的陷阱,不要上当。

当你正确地理解了什么是「决策」:它是对未来的赌注,它们不是「正确的」或「错误的」 ,而是基于当前信息做得一个决定,最终的结果是不是想要的并不仅仅取决于这个决定。

当你还正确地理解了什么是人固有的缺陷:比如你会有「自利性偏差」或者「动机性推理」,你还知道了怎么去避免或者至少是减轻它们对决策质量的影响。

是不是你就可以无往不利了呢?

我觉得不是的。你还需要理解「风险」以及怎么去控制它。

创业也好,生活也好,不是玩德州,随时可以再来一把。所以,虽然你应该训练自己不要用结果来衡量自己的决策是「正确」或者「错误」,但如果决策带来的坏结果可能会压垮你,那你就不能做这样的决定。

简单来说,那种可能让你下桌的赌注,最好别投。

怎么做

好的,我们认识并且接受世界充满了不确定性,也了解了人从构造上就很不容易处理不确定性,那么怎么过得更好一点呢?

独立思考

爱因斯坦在十多岁的时候,就思考如果追逐空间中的一束光,会有什么结果。因为按照当时的理论,如果能追上光,就意味着空间中的光像冻结了一样。但是,光不会被冻结。因此,光的速度不会慢下来,仍然以光速运动:这里出现的矛盾,最终促使他提出了狭义相对论。

在科学领域,质疑现有的结论和公认的假设是如此重要,以至于大量优秀学者做事的方式就是寻找现有理论无法自圆其说的地方,然后试图搞明白哪里到底发生了什么。

但习惯独立思考的回报远远不止在科学领域。流行的观点就像流行的衣服,大部分人只能跟着潮流引领者。传统观念对聪明人的束缚很小,他们往往不穿流行的衣服,也能够去挑战流行观点的漏洞,并且运用所知所学在解决问题的过程中得到的复利:思考越多,学到的就越多; 学到越多,能做的就越多; 能做的越多,机会就越多。

另外需要理解的是,独立思考并不意味着你需要畅所欲言,有些观点你未必需要说出来,因为这需要很大的勇气和担当。

勇气和担当

在大部分领域,回报与风险成比例。所以做很多真正有意义的事情都是有很大风险的:你会花费大量的精力在那些看起来毫无希望的事情上。

面对不确定性的一个解决方案是对冲你的赌注,也就是遵循明显更有希望的道路去探索。但是和任何对冲一样:当你降低风险的时候,你就在降低回报。我相信,做出这样选择的人,比为了自己的赌注努力拼搏,最终一无所获的人,多得多。

另一个解决方法是让自己对很多不同的事情感兴趣并投入赌注。但这里也有一个危险: 如果你从事太多不同的项目,你可能无法深入到其中任何一个。不过,历史上,在每个领域的深度还没有那么深的时候,还是有很多人因为这样去做获得了丰厚的回报:比如牛顿,在炼金、科学和寻找上帝三件事情上他都投入巨大,虽然只有科学方面的成就是真正有意义的。

在现代社会,每个领域的深度已经非常可怕。所以,勇气是至高无上的东西,加上担当简直就是人类最稀有的品格。

把缺陷转化为机会

人、事物、系统,都是有缺陷的。

如果你客观地观察,就会发现最大的优点往往都带来最大的缺点。拿人来说,聪明可能就骄傲,忠厚可能就愚钝,灵活可能就浅薄:它们只不过是事物的两面而已。

人如果比较敏锐是很容易感知到这些缺陷的,于是就很容易有情绪。

大量优秀的人才,都很容易陷入这种情绪,然后和自己或者和体制进行鲜血淋漓的斗争。

很悲壮,但也浪费了自己的天赋和宝贵的时间。

正确的做法是学会接受那些「不完美」,在消极的一面里看到积极的一面,并把它们转化成优势。

达尔文在他的自传中写道,他写下每一个和他的理论相矛盾的证据,以免这些矛盾点被他自己忘记。然后,他先把它们放一下,努力去推进自己的理论。等有了比较大的进展,再回过头来看如何解释那些矛盾点,或者如何改变理论来适应它们。

要学会把缺陷转化成机会,需要学会驾驭这样的模糊度。

如果因为不确定性,因为看到了缺陷就过度怀疑,你可能就没法继续前进,甚至根本不会开始; 如果你盲目相信,忽视了不确定性和明显的缺陷,那你可能蒙头狂奔,甚至万劫不复。

解决真正感兴趣并且重要的事情

我们小时候都听过,成功等于天赋加后天的努力。

没有人会对在石板上写数列有兴趣,除非你有拉马努金那样的数学天赋。

没有人可以先花上几年努力为自己的小说里的角色构建各自的语言再开始写小说,除非你像托尔金那样对它充满兴趣。

所以我觉得天赋和努力,两者其实可以统一成一个东西:兴趣。

兴趣是对事物强迫症般的,不计较意义的专注和投入。

不计较意义意味着他们这样做并不是为了给我们留下深刻印象或者让自己变得富有,而是为了他们自己。

这已经很好的对抗了不确定性:前面说了风险和回报成正比,而当人干的事情是为了自己,好像就可以接受任何回报。

但兴趣里面的非功利性虽然是有助于人拥有勇气去真诚地付出,但拉马努金和一个对手游痴迷的人,显然还是有区别的。

数列很重要,而手游可能不重要。

Humming 说过,每天中午,贝尔实验室的科学家们都在一张桌子上吃饭。他常常会问一个不认识的人:

「你们这个领域最重要的问题是什么? 你正在解决什么重要的问题?」

对方通常会说「好像也没有什么特别重要的」,他就会问「如果你所做的事情不重要,那你为什么还在贝尔实验室工作呢? 」

这让很多人不愿意和他吃饭甚至不愿意和他聊天。但也有几个人在获得了诺贝尔奖之后来感谢他:「是你的问题让我有勇气去做出伟大的工作,哪怕可能会浪费大量的时间」。

什么是重要的呢?如果有办法确定,可能它早就被很多人盯上,也就变得没那么重要了。

但是有一些办法好像是可以用来检验一个想法是否是重要的。 比如,你感兴趣的东西并不存在,你是在创造一些东西,而不是仅仅消费别人创造的东西(游戏、商品、服务),那么它会更有前途。比如,如果你感兴趣的事情很困难,尤其是对其他人来说比你更困难,那么它会更有前途。再比如,其他有天赋的人们也在痴迷要去解决的事情,那么它会更有前途。

但你永远不能确定。

投下你的赌注

面对不确定性,如何智慧地使用手里的赌注,并心平气和地接受结果,是很难的。

不妨接受,为了获得回报,你会浪费大量的赌注;为了做出伟大的东西,你会浪费大量的时间。这时间一开始甚至都不是浪费在你的兴趣上,而是浪费在不断尝试并且确认自己的兴趣在哪里的过程中。

但人只有这一辈子。就算是你相信轮回,按照那个理论体系,你这辈子对下辈子的影响也非常有限。这篇文章里面提到的很多东西都是很难的,但也是值得去尝试的。一旦你掌握了它们,在你感觉最沉重,事情看起来十分棘手或者毫无希望的时候,你就仍然可以坦然地去面对,去拼搏。

这本身就有意义,甚至这本身就是全部的意义。