当我写这篇文章的时候,离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,这显然不是我们想要的。
此时,就要用到绝对路径啦:
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的升级之路还算顺利,走在技术前沿的自豪感油然而生。