如何将你的应用迁移到Python3的三个步骤


Posted in Python onDecember 22, 2019

Python 2.x 很快就要 失去官方支持 了,尽管如此,从 Python 2 迁移到 Python 3 却并没有想象中那么难。我在上周用了一个晚上的时间将一个 3D 渲染器的前端代码及其对应的 PySide 迁移到 Python 3,回想起来,尽管在迁移过程中无可避免地会遇到一些牵一发而动全身的修改,但整个过程相比起痛苦的重构来说简直是出奇地简单。

每个人都别无选择地有各种必须迁移的原因:或许是觉得已经拖延太久了,或许是依赖了某个在 Python 2 下不再维护的模块。但如果你仅仅是想通过做一些事情来对开源做贡献,那么把一个 Python 2 应用迁移到 Python 3 就是一个简单而又有意义的做法。

无论你从 Python 2 迁移到 Python 3 的原因是什么,这都是一项重要的任务。按照以下三个步骤,可以让你把任务完成得更加清晰。

1、使用 2to3

从几年前开始,Python 在你或许还不知道的情况下就已经自带了一个名叫 2to3 的脚本,它可以帮助你实现大部分代码从 Python 2 到 Python 3 的自动转换。

下面是一段使用 Python 2.6 编写的代码:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
mystring = u'abcdé' 
print ord(mystring[-1])

对其执行 2to3 脚本:

$ 2to3 example.py 
RefactoringTool: Refactored example.py 
--- example.py   (original) 
+++ example.py   (refactored) 
@@ -1,5 +1,5 @@ 
 #!/usr/bin/env python 
 # -*- coding: utf-8 -*- 
 
-mystring = u'abcdé' 
-print ord(mystring[-1]) 
+mystring = 'abcdé' 
+print(ord(mystring[-1])) 
RefactoringTool: Files that need to be modified: 
RefactoringTool: example.py

在默认情况下,2to3 只会对迁移到 Python 3 时必须作出修改的代码进行标示,在输出结果中显示的 Python 3 代码是直接可用的,但你可以在 2to3 加上 -w 或者 --write 参数,这样它就可以直接按照给出的方案修改你的 Python 2 代码文件了。

$ 2to3 -w example.py 
[...] 
RefactoringTool: Files that were modified: 
RefactoringTool: example.py

2to3 脚本不仅仅对单个文件有效,你还可以把它用于一个目录下的所有 Python 文件,同时它也会递归地对所有子目录下的 Python 文件都生效。

2、使用 Pylint 或 Pyflakes

有一些不良的代码在 Python 2 下运行是没有异常的,在 Python 3 下运行则会或多或少报出错误,这种情况并不鲜见。因为这些不良代码无法通过语法转换来修复,所以 2to3 对它们没有效果,但一旦使用 Python 3 来运行就会产生报错。

要找出这种问题,你需要使用 Pylint 、 Pyflakes (或 flake8 封装器)这类工具。其中我更喜欢 Pyflakes,它会忽略代码风格上的差异,在这一点上它和 Pylint 不同。尽管代码优美是 Python 的一大特点,但在代码迁移的层面上,“让代码功能保持一致”无疑比“让代码风格保持一致”重要得多。

以下是 Pyflakes 的输出样例:

$ pyflakes example/maths 
example/maths/enum.py:19: undefined name 'cmp' 
example/maths/enum.py:105: local variable 'e' is assigned to but never used 
example/maths/enum.py:109: undefined name 'basestring' 
example/maths/enum.py:208: undefined name 'EnumValueCompareError' 
example/maths/enum.py:208: local variable 'e' is assigned to but never used

上面这些由 Pyflakes 输出的内容清晰地给出了代码中需要修改的问题。相比之下,Pylint 会输出多达 143 行的内容,而且多数是诸如代码缩进这样无关紧要的问题。

值得注意的是第 19 行这个容易产生误导的错误。从输出来看你可能会以为 cmp 是一个在使用前未定义的变量,实际上 cmp 是 Python 2 的一个内置函数,而它在 Python 3 中被移除了。而且这段代码被放在了 try 语句块中,除非认真检查这段代码的输出值,否则这个问题很容易被忽略掉。

try: 
  result = cmp(self.index, other.index) 
except: 
  result = 42 
  
return result

在代码迁移过程中,你会发现很多原本在 Python 2 中能正常运行的函数都发生了变化,甚至直接在 Python 3 中被移除了。例如 PySide 的绑定方式发生了变化、importlib 取代了 imp 等等。这样的问题只能见到一个解决一个,而涉及到的功能需要重构还是直接放弃,则需要你自己权衡。但目前来说,大多数问题都是已知的,并且有 完善的文档记录 。所以难的不是修复问题,而是找到问题,从这个角度来说,使用 Pyflake 是很有必要的。

3、修复被破坏的 Python 2 代码

尽管 2to3 脚本能够帮助你把代码修改成兼容 Python 3 的形式,但对于一个完整的代码库,它就显得有点无能为力了,因为一些老旧的代码在 Python 3 中可能需要不同的结构来表示。在这样的情况下,只能人工进行修改。

例如以下代码在 Python 2.6 中可以正常运行:

class CLOCK_SPEED: 
    TICKS_PER_SECOND = 16 
    TICK_RATES = [int(i * TICKS_PER_SECOND) 
           for i in (0.5, 1, 2, 3, 4, 6, 8, 11, 20)] 
class FPS: 
    STATS_UPDATE_FREQUENCY = CLOCK_SPEED.TICKS_PER_SECOND

类似 2to3 和 Pyflakes 这些自动化工具并不能发现其中的问题,但如果上述代码使用 Python 3 来运行,解释器会认为 CLOCK_SPEED.TICKS_PER_SECOND 是未被明确定义的。因此就需要把代码改成面向对象的结构:

class CLOCK_SPEED: 
    def TICKS_PER_SECOND(): 
        TICKS_PER_SECOND = 16 
        TICK_RATES = [int(i * TICKS_PER_SECOND) 
            for i in (0.5, 1, 2, 3, 4, 6, 8, 11, 20)] 
        return TICKS_PER_SECOND 
class FPS: 
    STATS_UPDATE_FREQUENCY = CLOCK_SPEED.TICKS_PER_SECOND()

你也许会认为如果把 TICKS_PER_SECOND() 改写为一个构造函数(用 __init__ 函数设置默认值)能让代码看起来更加简洁,但这样就需要把这个方法的调用形式从 CLOCK_SPEED.TICKS_PER_SECOND() 改为 CLOCK_SPEED() 了,这样的改动或多或少会对整个库造成一些未知的影响。如果你对整个代码库的结构烂熟于心,那么你确实可以随心所欲地作出这样的修改。但我通常认为,只要我做出了修改,都可能会影响到其它代码中的至少三处地方,因此我更倾向于不使代码的结构发生改变。

坚持信念

如果你正在尝试将一个大项目从 Python 2 迁移到 Python 3,也许你会觉得这是一个漫长的过程。你可能会费尽心思也找不到一条有用的报错信息,这种情况下甚至会有将代码推倒重建的冲动。但从另一个角度想,代码原本在 Python 2 中就可以运行,要让它能在 Python 3 中继续运行,你需要做的只是对它稍加转换而已。

但只要你完成了迁移,你就得到了这个模块或者整个应用程序的 Python 3 版本,外加 Python 官方的长期支持。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python牛刀小试密码爆破
Feb 03 Python
pytyon 带有重复的全排列
Aug 13 Python
在Linux系统上安装Python的Scrapy框架的教程
Jun 11 Python
Python求两个文本文件以行为单位的交集、并集与差集的方法
Jun 17 Python
Python中元组,列表,字典的区别
May 21 Python
Python实现简单过滤文本段的方法
May 24 Python
Python Unittest自动化单元测试框架详解
Apr 04 Python
基于python神经卷积网络的人脸识别
May 24 Python
将python包发布到PyPI和制作whl文件方式
Dec 25 Python
python ffmpeg任意提取视频帧的方法
Feb 21 Python
使用Keras中的ImageDataGenerator进行批次读图方式
Jun 17 Python
Python列表嵌套常见坑点及解决方案
Sep 30 Python
使用python快速实现不同机器间文件夹共享方式
Dec 22 #Python
python FTP批量下载/删除/上传实例
Dec 22 #Python
python 实现保存最新的三份文件,其余的都删掉
Dec 22 #Python
python中的split()函数和os.path.split()函数使用详解
Dec 21 #Python
python 操作hive pyhs2方式
Dec 21 #Python
python 消费 kafka 数据教程
Dec 21 #Python
python kafka 多线程消费者&手动提交实例
Dec 21 #Python
You might like
php 数组的一个悲剧?
2011/05/11 PHP
Yii2.0框架模型多表关联查询示例
2019/07/18 PHP
浅谈laravel-admin form中的数据,在提交后,保存前,获取并进行编辑
2019/10/21 PHP
Iframe 自适应高度并实时监控高度变化的js代码
2009/10/30 Javascript
在JavaScript中获取请求的URL参数[正则]
2010/12/25 Javascript
使用js声明数组,对象在jsp页面中(获得ajax得到json数据)
2013/11/05 Javascript
js字母大小写转换实现方法总结
2013/11/13 Javascript
js生成的验证码的实现与技术分析
2014/09/17 Javascript
js实现点击图片将图片地址复制到粘贴板的方法
2015/02/16 Javascript
javasript实现密码的隐藏与显示
2015/05/08 Javascript
微信小程序开发(一) 微信登录流程详解
2017/01/11 Javascript
xmlplus组件设计系列之路由(ViewStack)(7)
2017/05/02 Javascript
Vue列表页渲染优化详解
2017/07/24 Javascript
Bootstrap图片轮播效果详解
2017/10/17 Javascript
深入浅析vue组件间事件传递
2017/12/29 Javascript
node.js通过axios实现网络请求的方法
2018/03/05 Javascript
vue实现在表格里,取每行的id的方法
2018/03/09 Javascript
详解JavaScript中的数组合并方法和对象合并方法
2018/05/11 Javascript
微信小程序websocket实现聊天功能
2020/03/30 Javascript
解决vue A对象赋值给B对象,修改B属性会影响到A的问题
2018/09/25 Javascript
Vue 页面状态保持页面间数据传输的一种方法(推荐)
2018/11/01 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
layui复选框的全选与取消实现方法
2019/09/02 Javascript
Python 数据处理库 pandas 入门教程基本操作
2018/04/19 Python
在matplotlib的图中设置中文标签的方法
2018/12/13 Python
python cumsum函数的具体使用
2019/07/29 Python
Python实现TCP通信的示例代码
2019/09/09 Python
Flask 上传自定义头像的实例详解
2020/01/09 Python
Pytorch使用MNIST数据集实现基础GAN和DCGAN详解
2020/01/10 Python
python根据字典的键来删除元素的方法
2020/08/16 Python
Shopee马来西亚:随拍即卖,最佳行动电商拍卖平台
2017/06/05 全球购物
意大利男装网店:Vrients
2019/05/02 全球购物
在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排
2014/01/13 面试题
最新销售员个人自荐信
2013/09/21 职场文书
个人委托书怎么写
2014/04/04 职场文书
幼儿园中班班级总结
2015/08/10 职场文书