在 Windows 搭建Python多版本环境

高中开始听说Python,大概大三到大四之间尝试动手写。毕业后成为了一个Android码农,Python不是主力语言,零零星星写过一些顺手的小东西,而且主要在公司。

家里电脑本来就用得不多,还经历了多次重装。旧电脑全格了,从Win+Linux双系统变成了Linux专用;新电脑经历了升Win10之后几次系统崩溃C盘重置,后来又从32改64。

D盘的东西虽然还在,但各种配置乱得不行。这会要写Python,各种配置都要重新弄。反正都好几年前装的,干脆重新安装配置一遍。

不看不知道,果然很多东西都变了。


结论

正文写得很零碎,这里给个结论:

  • 如果你有同时用2、3的需求,分别安装2 和 3的最新版本(2.5这种古董还是别碰,焦头烂额),然后通过在代码开头写Shebang指定用哪个解析器执行。
  • 包管理用pip。作为Python包管理的事实标准,流行的包都会注册到pip。你知道包名之后,要用不过是一句 pip install 包名
  • 如果更进一步,你的不同项目之间要依赖不同版本的包,用virtualenv 来管理你的包。
  • 虽然Python用Notepad++ 写然后执行完全没有问题,但即使老司机也有大意的时候,新手更应该培养良好的编程规范,PyCharm 是个不错的选择,特别是Community版本免费,对于绝大多数人而言都是够用的了。

四年前写过一篇文章记录当时Win上配置Python2+3,现在看做法各种过时。譬如说2011年才发起的pip在当年我还不知道,现在已经成为了包管理器的事实标准。

pip

四年间不怎么关注Python的消息,居然也大概知道pip。然后查了一下目前主流的包管理器的对比,发现《Python 包管理工具解惑》 写得比较好。

不看不知道,包管理相关的工具已经有这么多!

  • distutils
  • setuptools
  • distribute
  • disutils2
  • distlib
  • pip

结论是pip已经成为了事实标准,所以默认情况下用pip,遇到特殊情况再考虑别的。详情看那篇文章,不再展开。(考虑到作者换博客等因素,文章链接可能失效,到时只好用标题搜搜看。)

Ubuntu

同样的事情我在Ubuntu上也折腾过一遍。Python对Linux友好太多,很简单的就弄完了。简单过一遍,用来跟Windows对照。(当然,简单还有一部分原因是,我没在Ubuntu装2.5)

没记错的话,Ubuntu自带Python2.7,所以我只要装Python3,然后分别装pip就好

1
2
3
$ sudo apt-get install python3
$ sudo apt-get install python-pip
$ sudo apt-get install python3-pip

这之后

1
2
3
4
5
6
7
8
9
10
$ python --version
Python 2.7.6
$ python3 --version
Python 3.4.3
$ pip --version
pip 1.5.4 from /usr/lib/python2.7/dist-packages (python 2.7)
$ pip2 --version
pip 1.5.4 from /usr/lib/python2.7/dist-packages (python 2.7)
$ pip3 --version
pip 1.5.4 from /usr/lib/python3.4/dist-packages (python 3.4)

Ubuntu 自带的pip版本太旧,要升级一下

warning : 如果是在Windows升级,请使用 python -m pip,原因详见 《升级 pip 时的一个坑》

1
2
3
4
5
6
7
8
$ pip install --upgrade pip
$ pip3 install --upgrade pip
$ pip --version
pip 1.5.4 from /usr/lib/python2.7/dist-packages (python 2.7)
$ pip2 --version
pip 8.1.2 from /usr/local/lib/python2.7/dist-packages (python 2.7)
$ pip3 --version
pip 8.1.2 from /usr/local/lib/python3.4/dist-packages (python 3.4)

这里就让我疑惑了,为什么唯独pip没有生效呢?我把PATH输出来

1
2
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:...

确实是local的在前面。而且我看了这三个名字的script,内容都是一样的。不过我还没搞清楚原因,第二天开机,就都变成8.1.2了。原因大概能猜到一二,但除非重头折腾,已经无法验证了。如果有大牛知道详细原因,欢迎留言告知。

看到这里,相信大家已经懂得,分别用 python V.S. python3,以及 pippip2 V.S. pip3 就可以调用对应的版本了。

果安装了更多的版本,譬如不止一个Python2,有2.5 和 2.7,那么还可以更具体地区分pip,那就是直接 pip-2.5 VS pip2.7。(pip某个版本以前,规则是pip-<ver>,之后变成了pip<ver>。支持2.5的都是老版本,所以带有横杠。详情可以查看 /usr/local/bin 里都有哪些文件。)

这是在bash执行 python xx.py 的情况,那如果是直接给执行权限当脚本跑呢? 写shebang!在脚本开头写

1
#! /usr/bin/python

1
#! /usr/bin/python3

Windows

双 Python for win

Windows 这边,我选择的组合是 Python 2.5 + Python 3.4。

选择2.5的原因是,虽然2.5 和 2.6/2.7 都是 2.x , 但只有 ≤2.5 才是纯粹的2.x, 2.6/2.7 其实是已经引入了 3.x 语法的过渡版本。
2.6/2.7 的代码固然不能在2.5上跑,而2.5 on 2.6/2.7 虽然大多跑得过,也往往存在某些不可预料的结果。(网上很容易搜到例子)
我主要还是写3.x的,所以两个系统都装了3.4,但是难免有需要跑一些陈年代码的时候,考虑到Ubuntu自带2.7,Windows就装2.5了。

都安装上是没有难度的,问题是运行。

这涉及到3种运行情况:

  1. 双击执行 *.py 文件(CLI,也就是纯CMD)
  2. 双击执行 *.py 文件(GUI,也就是通过PyQt一类库实现了界面的)
  3. 在CMD窗口里执行,同样分以上两种情况

两个版本都刚安装完的时候,默认的情况应该是:

1、2 都是哪个在后面安装就调用哪个。因为在Windows,双击执行靠的是文件类型关联,如果安装的时候都有执行文件关联的话(我记得默认这个是有选上的),那么后面的会覆盖前面的。
换言之,假设我主要用 3.x ,那么先装 2.x 再装 3.x ,那么双击运行的都是 3.x ,2.x 就调用不了了。

第3种情况,命令行里执行 python xxx.py , 如果你没有去系统的Path环境变量添加这两个Python的话,实际上系统会找不到 python。然后如果你往Path里 把两个的 bin 目录都加进去,由于搜索是从前往后寻找,命中就结束,所以是哪个在Path里靠前执行哪个,靠后不起作用。

随时指定版本

那么现在的问题就是,如何在不重新安装,以及不修改Path的值的情况下,随时指定Python的版本。(四年前我的方案是,新建一个环境变量放进Path,然后通过批处理修改该变量的指向来解决情况3,没有解决1、2。当然四年前也还没有PEP397)

PEP397 给出了官方解决方案。

详细内容大家可以点进去自己看。核心内容是,安装3.3 以上版本时(这样才包含PEP397),会在 C:\Windows\下放一个启动器 py.exe,通过启动器运行,可以用参数指定启动的版本。

命令行参数指定

1
> py-2 xxx.py

1
> py-3 xxx.py

当然,如果你装的版本更多,你可以指定得更详细,譬如

1
> py-3.3 xxx.py

但是如果指定的版本不存在,那么就会报错

1
Requested Python version (3.3) not installed

本质上,py.exe 只是接收了第一个参数,然后查找参数对应的版本的 python.exe,然后把剩下的参数转给它。同样在 C:\Windows\下另外还有一个 pyw.exe 对应 pythonw.exe

不过每次都要指定也太烦了,每次运行前都要先想想这是哪个版本的代码,这个其实不是在写的时候已经决定好了吗?跟Linux类似地,还是写shebang。

Shebang 指定

个人认为Windows 下面的,严格来说应该不算shebang,只是模仿了这种形式。毕竟在Linux下,shebang所以是写给shell解析的,而这里其实是给 py.exe 解析的。

1
#!python2

1
#!python3

如果我们写的是跨平台的代码,Windows和Linux都要跑呢?这种伪shebang 在 Linux并不被接受。

1
bash: ./test_shebang.py: python3: 解释器错误: 没有那个文件或目录

很简单,两边都写Linux风的shebang就好了。即使Windows下并不存在 /usr/bin/python3 这么个东西,一样可以运行。(所以才说它是个伪shebang,其实只是给py 解析的参数而已)

1
#!/usr/bin/python3

双击执行

双击执行当然没办法给 -2 这样的参数,所以要在代码里面写上shebang。

同时为了让它生效,必须确认 .py 文件关联的是 py.exe,而不是某个版本的 python.exe
对着某个 .py 文件,右键查看属性 -> 打开方式,确认一下。
如果最后安装的是Python3.3 以上的版本,貌似会自动给关联 py.exe。不过不是也没关系,自己改过来就好。

类似的,如果是GUI程序,可以把后缀改为 .pyw,同时关联 pyw.exe
嗯……其实区别只是,pyw.exe 会将调用转给 pythonw.exe,跟 python.exe 相比,运行时不会显示命令行黑窗 (因为有GUI给你看嘛,还看什么小黑窗)。调试的时候,可以临时把文件名改为 .py 看输出。
不过,个人更推荐使用 logging 输出详细的调试信息。

pip for win

要庆幸的是,Python3.4 for win 已经自带了 pip。所以我只要为2.5安装即可。

实测发现,2.5实在太旧了(2.5的时代还没有pip吧),用pip自动安装2.5下的包,基本都会出错(其实是选了声明支持2.x 的包,结果里面有2.6以上的语法,会出错),所以给2.5安装pip实用价值不大。 我装2.5 本来就是为可能的测试预留的,没有必要花大力气,真有需要也可以setuptools 安装,所以最后放弃了折腾。

如果是Windows下的2.7 安装pip:

  1. 安装对应版本的setuptools
    setuptools ,留意不同版本的安装方法略有差别
  2. pip ,下载对应的pip包并解压,在解压后的目录执行 > py -2 setup.py install

这之后,就可以

1
>py -2 -m pip

1
>py -3 -m pip

不方便的地方是,没有 pip2 / pip3 ……
其实是有的,不过要先将各个版本安装目录下的 Scripts 目录加入Path 环境变量。

加入之后,pip 指向的版本,取决于你将哪个目录放在前(因为两边都有pip,看哪个先命中),然后具体指定版本,就要看Scripts下都有那些文件了。3.x 下应该 pip3pip3.x都有,但是2.5 下 只有 pip-2.5 没有 pip2 (其实跟上面Linux下的情况基本一致)。

Virtualenv

暂时对于多版本环境需要不是特别大,之后用到再详细写。


以下是针对2.5的操作,包含不必要的试错,主要是无用功,留作供我日后参考。

跟2.7类似,也是两步

  1. 安装setuptools。因为支持2.5的setuptools是1.x版本,不能用最新的,而要用这个链接 Setuptools 1.x

  2. 下载最新的支持Python2 的pip包,当前这个时刻是8.1.2,解压,在解压后的目录执行 > py -2 setup.py install

结果报语法错误。仔细一看,原来 8.1.2 最老只支持 2.6,重新找,又下载了 支持 2.5 的 1.4.1。结果依然有语法错误。但是这次支持版本信息没错啊,只好自己去看报错信息。

看代码的结论是,pip1.4.1 声称支持 Python2.5,它还是引入了Python2.6 为 Python3 准备的过渡语法 b'',以至于一运行就有错误。实际上支持2.5的最新pip版本是 pip1.3.1

折腾至此,实际上已经费了好多时间和精力,总算把 pip1.3.1 给 2.5 安上了。那么我就来安装一些包看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>pip-2.5 install virtualenv
Downloading/unpacking virtualenv
Exception:
...
...
raise NoSSLError()
NoSSLError:
###################################################################
## You don't have an importable ssl module. You are most ##
## likely using Python 2.5, which did not include ssl ##
## support by default. In this state, we can not provide ##
## ssl certified downloads from PyPI. ##
## ##
## You can do one of 2 things: ##
## 1) Install this: https://pypi.python.org/pypi/ssl/ ##
## (It provides ssl support for older Pythons ) ##
## 2) Use the --insecure option to allow this insecurity ##
## ##
## For more details, go to the "SSL Certificate Verification" ##
## section located here: ##
## http://www.pip-installer.org/en/latest/logic.html ##
## ##
###################################################################

2.5 默认没有SSL包,而pip需要用SSL下载包……

好,我先安装SSL总可以吧

1
2
3
4
5
6
7
>pip-2.5 install ssl
Downloading/unpacking ssl
Exception:
...
...
raise NoSSLError()
NoSSLError:

变成了先有鸡还是现有蛋的死循环了…

那我关掉加密传输不要SSL总可以了吧…

1
2
3
4
5
6
7
8
9
10
11
12
>pip-2.5 install virtualenv --insecure
Downloading/unpacking virtualenv
Downloading virtualenv-15.0.2.tar.gz (1.8MB): 1.8MB downloaded
Running setup.py egg_info for package virtualenv
...\virtualenv\setup.py:51: Warning: 'with' will become a reserved keyword in Python 2.6
Traceback (most recent call last):
File "<string>", line 16, in <module>
File "...\virtualenv\setup.py", line 51
with open(os.path.join(here, *paths)) as f:
^
SyntaxError: invalid syntax
...

额,又是引进了 2.6 的语法,这次是关键字 with

看来 pip 对 2.5 并不友好。pip 的本意是包管理的自动化。如此折腾就违反初衷了。反正 2.5 不是用来开发用的,偶尔需要什么包,手动安装一下也不费什么事,所以就此打住,不折腾pip了。


知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
详情请点击查看协议具体内容。