首页 存档 技术 查看内容

移植到Python3

2018-3-30 13:00 |来自: 互联网 736 0

摘要: (点击上方公号,可快速关注) 作者:Armin Ronacher 译者:开源中国社区 链接:http://www.oschina.net/translate/porting-to-python-3-redux 经历移植jinja2到python3的痛苦之后,我把项目暂时放一放,因为我怕 ...

(点击上方公号,可快速关注)


作者:Armin Ronacher

译者:开源中国社区

链接:http://www.oschina.net/translate/porting-to-python-3-redux

经历移植jinja2到python3的痛苦之后,我把项目暂时放一放,因为我怕打破python3的兼容。我的做法是只用一个python2的代码库,然后在安装的时候用2to3工具翻译成python3。不幸的是哪怕一点点的改动都会打破迭代开发。如果你选对了python的版本,你可以专心做事,幸运的避免了这个问题。

来自MoinMoin项目的Thomas Waldmann通过我的python-modernize跑jinja2,并且统一了代码库,能同时跑python2,6,2,7和3.3。只需小小清理,我们的代码就很清晰,还能跑在所有的python版本上,并且看起来和普通的python代码并无区别。

受到他的启发,我一遍又一遍的阅读代码,并开始合并其他代码来享受统一的代码库带给我的快感。

下面我分享一些小窍门,可以达到和我类似的体验。

放弃python 2.5 3.1和3.2

这是最重要的一点,放弃2.5比较容易,因为现在基本没人用了,放弃3.1和3.2也没太大问题,应为目前python3用的人实在是少得可怜。但是你为什么放弃这几个版本呢?答案就是2.6和3.3有很多交叉哦语法和特性,代码可以兼容这两个版本。

  • 字符串兼容。2.6和3.3支持相同的字符串语法。你可以用 “foo” 表示原生字符串(2.x表示byte,3.x表示unicode),u”foo” 表示unicode字符串,b”foo” 表示原生字符串或字节数组。

  • print函数兼容,如果你的print语句比较少,那你可以加上”from __future__ import print_function”,然后开始使用print函数,而不是把它绑定到别的变量上,进而避免诡异的麻烦。

  • 兼容的异常语法。Python 2.6引入的 “except Exception as e” 语法也是3.x的异常捕捉语法。

  • 类修饰器都有效。这个可以用在修改接口而不在类结构定义中留下痕迹。例如可以修改迭代方法名字,也就是把 next 改成 __next__ 或者把 __str__ 改成 __unicode__ 来兼容python 2.x。

  • 内置next调用__next__或next。这点很有用,因为他们和直接调用方法的速度差不多,所以你不用考虑得太多而去加入运行时检查和包装一个函数。

  • Python 2.6 加入了和python 3.3接口一样的bytearray类型。这点也很有用,因为2.6没有 3.3的byteobject类型,虽然有一个内建的名字但那仅仅只是str的别名,并且使用习惯也有很大差异。

  • Python 3.3又加入了byte到byte和string到string的编码与解码,这已经在3.1和3.2中去掉了,很不幸,他们的接口很复杂了,别名也没了,但至少更比以前的2.x版本更接近了。

最后一点在流编码和解码的时候很有用,这功能在3.0的时候去掉了,直到3.3才恢复。

没错,six模块可以让你走得远一点,但是不要低估了代码工整度的意义。在Python3移植过程中,我几乎对jinja2失去了兴趣,因为代码开始虐我。就算能统一代码库,但还是看起来很不舒服,影响视觉(six.b(‘foo’)和six.u(‘foo’)到处飞)还会因为用2to3迭代开发带来不必要的麻烦。不用去处理这些麻烦,回到编码的快乐享受中吧。jinja2现在的代码非常清晰,你也不用当心python2和3的兼容问题,不过还是有一些地方使用了这样的语句:if PY2:。

接下来假设这些就是你想支持的python版本,试图支持python2.5,这是一个痛苦的事情,我强烈建议你放弃吧。支持3.2还有一点点可能,如果你能在把函数调用时把字符串都包装起来,考虑到审美和性能,我不推荐这么做。

跳过six

six是个好东西,jinja2开始也在用,不过最后却不给力了,因为移植到python3的确需要它,但还是有一些特性丢失了。你的确需要six,如果你想同时支持python2.5,但从2.6开始就没必要使用six了,jinja2搞了一个包含助手的兼容模块。包括很少的非python3 代码,整个兼容模块不足80行。

因为其他库或者项目依赖库的原因,用户希望你能支持不同版本,这是six的确能为你省去很多麻烦。

开始使用Modernize

使用python-modernize移植python是个很好的还头,他像2to3一样运行的时候生成代码。当然,他还有很多bug,默认选项也不是很合理,可以避免一些烦人的事情,然你走的更远。但是你也需要检查一下结果,去掉一些import 语句和不和谐的东西。

修复测试

做其他事之前先跑一下测试,保证测试还能通过。python3.0和3.1的标准库就有很多问题是诡异的测试习惯改变引起的。

写一个兼容的模块

因此你将打算跳过six,你能够完全抛离帮助文档么?答案当然是否定的。你依然需要一个小的兼容模块,但是它足够小,使得你能够将它仅仅放在你的包中,下面是一个基本的例子,关于一个兼容模块看起来是个什么样子:

import sys

PY2 = sys.version_info[0] == 2

if not PY2:

text_type = str

string_types = (str,)

unichr = chr

else:

text_type = unicode

string_types = (str, unicode)

unichr = unichr

那个模块确切的内容依赖于,对于你有多少实际的改变。在Jinja2中,我在这里放了一堆的函数。它包括ifilter, imap以及类似itertools的函数,这些函数都内置在3.x中。(我纠缠Python 2.x函数,是为了让读者能够对代码更清楚,迭代器行为是内置的而不是缺陷) 。

为2.x版本做测试而不是3.x

总体上来说你现在正在使用的python是2.x版本的还是3.x版本的是需要检查的。在这种情况下我推荐你检查当前版本是否是python2而把python3放到另外一个判断的分支里。这样等python4面世的时候你收到的“惊喜”对你的影响会小一点

好的处理:

if PY2:

def __str__(self):

return self.__unicode__().encode('utf-8')

相比之下差强人意的处理:

if not PY3:

def __str__(self):

return self.__unicode__().encode('utf-8')

字符串处理

Python 3的最大变化毫无疑问是对Unicode接口的更改。不幸的是,这些更改在某些地方非常的痛苦,而且在整个标准库中还得到了不一致地处理。大多数与字符串处理相关的时间函数的移植将完全被废止。字符串处理这个主题本身就可以写成完整的文档,不过这儿有移植Jinja2和Werkzeug所遵循的简洁小抄:

  • ‘foo’这种形式的字符串总指的是本机字符串。这种字符串可以用在标识符里、源代码里、文件名里和其他底层的函数里。另外,在2.x里,只要**这种字符串仅仅可使用ASCII字符,那么就允许作为Unicode字符串常量。

    这个属性对统一编码基础是非常有用的,因为Python 3的正常方向时把Unicode引进到以前不支持Unicode的某些接口,不过反过来却从不是这样的。由于这种字符串常量“升级”为Unicode,而2.x仍然在某种程度上支持Unicode,因此这种字符串常量怎么用都行。

    例如 datetime.strftime函数在Python2里严格不支持Unicode,并且只在3.x里支持Unicode。不过因为大多数情况下2.x上的返回值只是ASCII编码,所以像这样的函数在2.x和3.x上都确实运行良好。

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部