从 Python 2 迁移到 Python 3,只剩不到 1 个月的时间。幸运的是,迁移工作没想象中那么困难。
众所周知,对 Python 2 的支持将在 2020 年初停止。不管怎样,“板上钉钉”的是,最后一个主要的 2.7.x 版本将在 2020 年 4 月发布。之后,针对 Python 2 的所有开发都将停止,这意味着不再有安全更新。
目前,许多包的维护者都已迁移到 Python 3。其中,有些仍然支持 Python 2,而其他的则已经放弃。2020 年 4 月后,大多数软件包将停止支持 Python 2。
据悉,Python 3.0 发布于 2008 年 12 月 3 日。没错,我们曾有足够时间进行迁移。如果现在还没有行动,那就应该把它放在第一。你至少应该在 2020 年结束前迁移到 Python 3,否则就要面临各种风险,比如漏洞、无法运行的软件等。
如果不知道正使用的Python版本,你可以用下面的命令查看当前的Python版本:
$ python --version
Python 2.7.16
它适用于任何操作系统,包括Windows。希望你的答案是3.7.x,甚至是更高版本;即使是2.7.x,也先不要担心。许多系统都安装了两个版本的Python。通常来说,输入python
时会运行Python 2,输入python3
时运行Python 3。
试一下:
python3 --version
Python 3.7.6
如果得到类似输出,那么恭喜,你已经安装了Python 3。
我发现这有个很棒的网站,它明确地告诉你如何在Windows、Linux、macOS甚至Chromebooks上安装Python 3。去那里看看,收获会不小。
现在,你已经运行Python 3,就该升级代码了。代码升级并不难,你可以遵循多种策略。现在大多数的库都已经兼容Pyhton 3。
所以你只需要:
2to3
是一个Python程序,可以读取Python 2源码并进行一系列修复,将其转换为有效的Python 3代码。其标准库包含一组丰富的修复程序,几乎可以处理所有代码。
Python 3中的一个明显变化是print
现在变成了名为print()
的函数。例如,以下Python 2代码:
def greet(name):
print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)
可以通过调用进行转换
$ 2to3 greet.py
默认情况下,这只会将差异部分显示在屏幕上。不过在检查后,你可以用-w
选项,它就会真的更改文件了:
$ 2to3 -w greet.py
原始文件已被更改,而旧文件将被保存为greet.py.bak
。结果如下:
def greet(name):
print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)
2to3
还有一些更有趣的选项:
-l
—— 列出所有的修复程序-x
—— 排除选择的修复程序-f
—— 只运行指定的修复程序在开始代码转换之前,请继续并阅读完整文档。
six
是一个Python 2和3的兼容库,它可以帮助代码库同时支持Python 2和3。我建议使用2to3
完全迁移到Python 3,但如果你不能这么做(无论出于什么原因),至少它可以做到让你的代码库在两个版本上都能用。
six提供的函数能消除Python 2和3之间的语法差异。一个简单的例子是six.print_()
,在Python 3中打印是通过print()
函数完成的,在Python 2中print
没有括号。因此,通过使用six.print_()
,你可以用一条语句同时支持两个语言版本。
实际上:
six
的名字出自于2乘3等于6future
你可能需要升级依赖的包。对每个正在使用的包,检查它是否已经支持Python 3。
如果没有,就去找支持的版本。你可能需要改动某些代码,因为API往往会随着时间改变。
迁移代码后,可以在代码中检查Python版本。这样可以确保你和用户没有在不兼容的版本下运行脚本,否则会导致难以理解的错误。像这样做一个简单的检查:
if not sys.version_info > (2, 7):
# 批评下还在用十年前版本python的用户
elif not sys.version_info >= (3, 5):
# 告诉用户他(她)需要升级,因为你用到了3.5版本的特性
为进一步提起你的胃口,以下是Python 3具备的一些优点。
好处在于:
print
作为一个语句存在,如果print
是一个函数会更加统一。print
是一个函数,因此可以将其作为参数传递给需要它的函数。以一个函数为例,它需要另一个函数进一步处理数据作为参数。为简单地对它实现模拟/调试,你现在能直接传递一个print()
函数。print
:[print(x) for x in range(10)]
,因为现在它是一个函数。builtins.print
来覆写print
函数,而对于语句就不能这么做了。在Python 3中,每个字符串默认都是Unicode字符串。在Python 2中,字符串默认为ASCII字符串,这限制了它可以处理的字符范围。如果需要Unicode字符串,则必须这样创建:
# Python 3中不再需要
unicode_sting = u'Ümlaut? Nō prōblem!'
这在许多国家是必需的。
从3.7版本(相当新版本)开始,Python提供数据类(data classes)。与常规类或其他替代方法相比,它有一些优点,例如返回多个值或字典:
__eq__
,所以可以对比数据类(data classes)__repr__
,可以轻松打印出一个数据类以下是实践中数据类(data classes)的示例:
from dataclasses import dataclass
@dataclass
class Card:
rank: str
suit: str
card = Card("Q", "hearts")
print(card == card)
# True
print(card.rank)
# 'Q'
print(card)
Card(rank='Q', suit='hearts')
从Python 3.5起,合并字典变得更容易:
dict1 = { 'a': 1, 'b': 2 }
dict2 = { 'b': 3, 'c': 4 }
merged = { **dict1, **dict2 }
print (merged)
# {'a': 1, 'b': 3, 'c': 4}
如果存在重叠的键,则第一个字典中的键将被覆盖。
在Python 2中,除法运算符(/
)默认为整数除法,除非操作数之一是浮点数。 因此有以下行为:
# Python 2
5 / 2 = 2
5 / 2.0 = 2.5
在Python 3中,除法运算符默认为浮点除法,//
运算符变为整数除法。 这样我们会得到:
Python 3
5 / 2 = 2.5
5 // 2 = 2
关于这项更改背后的动机,你可以读一读PEP-0328。
在Python 2中,可以对所有内容进行比较。 以下示例将全部返回True:
print "a string" > 2
print None < 5
它没有意义,并且可能将错误隐藏。在Python 3中,这些比较将引发TypeError
异常。
Python 2有两个range
函数:range
和xrange
,后者更快,因为它的实现基于迭代器。
在Python 3中,range
已变为xrange
,并且删除了xrange
名称。在这个地方,Python减少了学习者的困惑。
对于Python 3 vs. Python 2的话题,我可以一直谈下去,但现在你应该知道该做什么。
2020年4月后,放弃对Python 2支持的简单事实应该足以让你采取行动,转换那些代码库。
英文原文:
领取专属 10元无门槛券
私享最新 技术干货