Python3 迁移到新版本的技巧[Python3 学习笔记]

1,033 阅读4分钟

Python是一个快速发展的语言,每年都会产生一个主版本并且每个主版本的生命周期为5年.

频繁的年度更新和相对短的生命周期使开发人员必须2到3年就要考虑一次Python主版本的迁移.表面看5年看起来不算短,但是对软件开发来讲,一个大中型的项目的生命周期(开发和维护)是比5年要长的多的,可能一个项目开发了大半年然后上线试用并在一年内正式开始商用,接着处于3年左右的维护/迭代期,最后一年就就迫切需要考虑当时Python主版本即将结束生命周期的问题了,而这时基本上主版本已经更新了至少3次了.

就当前软件开发的情况而言,最好的方式就是走持续迭代的方式,开发人员在日常的学习中锚定在最新的主版本上,然后实际的业务开发中和线上环境锚定在次新版本上,保持业务代码和较新版本的同步. 以往的跨越式的大版本升级带来的工作量和风险往往比前者高很多.

这是Python版本的状态表:status of python branches

Python 的向前兼容性问题

Python这类解释型的语言相比Java这类编译型语言在原理上就缺乏较强的代码静态检查能力,某行前后不兼容的业务代码可能需要在被执行的时刻才能暴露出来,而Java这类编译型的语言则可以在编译期就产生错误或者警告,Java代码(或者Groovy/Scala/Kotlin)会被编译为称为bytecode的中间码,JVM运行bytecode而不是执行运行Java代码,实现了底层JVM和上层Java代码的解耦. 所以以Java和Python为例,Java的向后兼容性比Python好很多,一份多年前的程序基本无需编译就可以直接在新版本的JVM上运行或者需要编译但是在编译期就能检查出来明显的兼容性问题.相比之下这个段时间内Python早已经产生了多个主版本的迭代,一份旧的python在新的python环境下可能已经累积使用了很多过时/弃用的旧特性,产生了一些兼容性问题.

可靠的迁移代码到新版本

Python可靠迁移手段基本都是事后检查类型的,也就是讲你需要结合非常简单的静态代码检查,然后代码运行期持续的检查运行期的状态并做好很好的单元测试工作.

运行时警告

Python自己附带了一个非常有用的启动参数用-W.当你由低版本迁移到新版本的时候,你可以使用-W参数来运行你的代码,它能让你及时发现当前版本相对以前的版本弃用/移除了那些特性. 当你当前的代码使用了新版本标记为弃用/移除的过时特性时,它会及时的输出警告信息辅助你进行代码调整工作.

-W可以设置为多个级别.比如-Werror可以在将警告性的兼容性问题转换为错误,这样能强制你进行代码的调整工作.

例如下面的程序

import threading

if __name__ == '__main__':
    # currentThread在Python 3.10被弃用
    thread = threading.currentThread()
    print(thread.name)

当我们在Python 3.10中使用-W运行时如下

PS C:\workspace\viper\test> Python -V
Python 3.10.0

# -Wdefault只会产生警告但是不中断代码运行
PS C:\workspace\viper\test> python -Wdefault .\temp.py
C:\workspace\viper\test\temp.py:4: DeprecationWarning: currentThread() is deprecated, use current_thread() instead
  thread = threading.currentThread()
MainThread

# -Werror产生错误并中断代码运行,强制你进行代码调整来匹配当前的Python环境
PS C:\workspace\viper\test> python -Werror .\temp.py
Traceback (most recent call last):
  File "C:\workspace\viper\test\temp.py", line 4, in <module>
    thread = threading.currentThread()
  File "C:\INSTALL\Python3\lib\threading.py", line 1429, in currentThread
    warnings.warn('currentThread() is deprecated, use current_thread() instead',
DeprecationWarning: currentThread() is deprecated, use current_thread() instead
PS C:\workspace\viper\test>

所以上面的例子我们可以知道有了-W,我们在迁移旧代码到新的Python环境时,先运行然后在运行期收集兼容性问题是一个有效的迁移保障性手段. 当你收集到一些兼容性问题后可以批量的对重复性的过时代码做批量的调整.

其他补充手段

  • 静态代码检查: 可以使用一些常规的静态代码检查工具做一些初步的兼容性筛选,这是事前手段,能检查到一些非常明显的问题
  • 代码单元测试: 最好是对每个API和关键性的函数都编写测试用例,在自动化测试的事后就能尽量的确保业务代码基本都被运行过,这样和-W运行参数结合效果最好