Python3 升级记

从Python2到Python3

Posted by Sylvia on February 1, 2018

当我写这篇文章的时候,离python2退休只有两年两个月零十天了,而当前的项目仍然使用python2的环境,被时代淘汰的紧迫感以及python2中类似编码的坑催促着我们来一场说升就升的级。下面就来分享一下在从python2到python3的升级过程以及遇到的坑吧。

###1. 环境切换

step 1. 安装python3

强烈不建议不通过官网下载python3,我用的MacOS的homebrew下载的python3,环境变量配起来麻烦还容易出错。
大家可以通过官网 https://www.python.org/downloads/ 下载哦,我下的版本是3.6.4。

step 2. 虚拟环境切python3

我们需要的是python2到3的平滑升级,而且毕竟之前有很多用python2语法的项目,显然需要有一个工具可以简单的切换python2和python3,这里我们使用了virtualenv。
终端切到需要使用python3的项目,进行以下操作:

# 将原先的python2虚拟环境失效
deactivate origin_env

# 重建一个新的虚拟环境
virtualenv env3.6 --python=python3

# 使新环境生效
source env3.6/bin/activate

# 安装依赖
pip install requirements

平滑切换至python3~

2.代码修改

由于python2和python3的语法差异比较大,从python2升级到python3后我们还需要修改一下代码。

1. 引入修改

升级后首先就发现许多from … import … 出现了红线,这是由于python2在引入时首先会搜索当前目录下的文件,当找不到时再去根目录下搜索,随后去python搜索路径sys.path中搜索。而python3有些不同,它会直接在根目录下搜索再去python搜索。
假设我们的项目有这样的路径:

tests/
|
+--__init__.py
|
+--constants.py
|
+--models.py
|
+--case.py

当我们用python3在case.py中需要引入整个constants.py,另外还需要引入models.py的一个类,我们需要这样操作:

from . import constants
from .models import Model_A
2. os.path -> pathlib

当然,在python3下os.path也可以使用,但为了更彻底地使用python3,我们使用了python3的pathlib。
首先 pip install pathlib 进行安装哦,随后我们就可以使用pathlib操作啦~

项目配置文件(config.py)里往往都会有个获取项目根目录路径的全局变量ROOT_PATH,原先我们用以下代码来操作:

ROOT_PATH = pathlib.Path(__file__).cwd()

但执行用例后发现,这个ROOT_PATH并不是配置文件的路径,而且被执行用例的working directory。就比如说,我在tests/case.py中调了根目录下config.py里的ROOT_PATH,debug后发现ROOT_PATH竟然指向tests文件夹。查看case.py的配置文件(Run -> Edit Configuration),发现ROOT_PATH就指向其中的working directory,这显然不是我们想要的。 Edit Configuration

此时,就要用到绝对路径啦:

ROOT_PATH = Path(__file__).resolve().parent

Path(__file__).resolve()指向config.py的路径,而后面加一个parent则指向config.py的上层目录(即根目录的地址)。

之前用os.path时,我们会使用path.join来进行路径的拼接,那么pathlib里怎么操作呢?

p = Path.PurePath('foo', 'tests/cases')
print (p)    # 打印出'foo/tests/cases'

也可以酱紫:

p = Path.joinpath('foo', 'tests/cases')
print (p)

更详细的关于Path的介绍,可以参考这篇哦,Python3 操作系统与路径 模块(os / os.path / pathlib)

3. 数据类型

python2里的数据类型有int,long,float和complex,而python3不支持long类型了,其只有一种整数类型int,表示为长整型。=__= 默默把代码里的long全部改掉…
而在python2中的最大整型sys.maxint也要变成sys.maxsize咯。

4. map(), reduce(), filter()

当我改好上述的代码觉得万事大吉的时候,有一个case总也跑不过,我默默的比对了好几遍数据,怎么也不会想到是map()的问题。最后当我一条条数据debug时,才发现map()的返回怪怪的,在python2中,map()的返回是一个list,而python3中map()的返回却是一个iterable的对象,我们还需要将这个对象转换成list。

与此类似的,build-in函数filter()和zip()的返回结果也需要转换成list。

同样,reduce()也不能直接用了,需要从functools引入一下哦:

from functools import reduce
reduce(a,b,c)
5. http,urllib标准库

由于一直用的requests库发http请求,并未过多的使用http相关的模块,但是作为一些常用的标准库,这里还是需要提及一下。
python2中我们常用的httplib、 Cookie、 cookielib在python3中代替为http.client、http.cookies、http.cookiejar,几个包合并成了一个http包,引入的时候也应更加方便了吧。

Python 2 Python 3
import httplib import http.client
import Cookie import http.cookies
import cookielib import http.cookiejar

python2中用来分析、编码和获取URL的模块在python3中也统一组合成了一个单独的包urllib。

Python 2 Python 3
import urllib import urllib.request, urllib.parse, urllib.error
import urllib2 import urllib.request, urllib.error
import urlparse import urllib.parse
import robotparser import robotparser

踩了几个小坑后,python的升级之路还算顺利,走在技术前沿的自豪感油然而生。