编写同时兼容Python2.x与Python3.x版本的代码的⼏个⽰
编写兼容Python2.x与3.x代码
当我们正处于Python 2.x到Python 3.x的过渡期时,你可能想过是否可以在不修改任何代码的前提下能同时运⾏在Python 2和3中。这看起来还真是⼀个合理的诉求,但如何开始呢?哪些Python 2 代码在 3.x 解释器执⾏时容易出状况呢?
print vs print()
如果你想的和我⼀样,你或许会说print语句,这是个很好的着⼿点,先简单展⽰⼀下,print在2.x中是⼀条语句,⽽在3.x中它是⼀个关键字或者是保留字。换句话说,因为这个变化涉及到语⾔的语法,你不可以使⽤在if语句中,Python仍然没有#ifdef 宏。下⾯尝试把括号⾥⾯的参数打印出来:
>>> print('Hello World!')
Hello World!
很酷,这个在Python2和Python3中都可以运⾏,⽽且运⾏的效果是⼀样的,再来看看下⾯这段:
>>> print(10, 20) # Python 2
(10, 20)
此时,你并没有像前⾯那样幸运得到⼀样的结果,Python2中打印的是元组(tuple),⽽在Python3中传递多个参数到print()⾥⾯时打印的是两个值:
>>> print(10, 20) # Python 3
10 20
如果你思考得⽐较多的话,我们可以检查print是否是⼀个关键字,keyword模块包含⼀个关键字列表。print在3.x中不是关键字,可以简单验证⼀下:
>>> import keyword
>>> 'print' in keyword.kwlist
False
作为⼀名聪明的程序员,你可能在2.x中尝试的时候期待的结果是True,尽管这并没有错,但是为了达到
Python3的效果,但你仍然会因为其他原因导致失败。
>>> import keyword
>>> if 'print' in keyword.kwlist:
...  from __future__ import print_function
...
File "", line 2
SyntaxError: from __future__ imports must occur at the beginning of the file
⼀种解决⽅案是使⽤⼀个函数,其功能类似于print,其中之⼀是sys.stdout.write(),另⼀个是distutils.log.warn()。不管出于什么原因,我们决定使⽤后者。“hello world”的例⼦看起来是这样的:
# Python 2.x
print 'Hello World!'
# Python 3.x
print('Hello World!')
下⾯的代码就可以在两个版本中通⽤:
# Python 2.x & 3.x compatible
from distutils.log import warn as printf
printf('Hello World!')
为什么我们不⽤sys.stdout.write()呢,因为我们需要添加⼀个NEWLINE字符在字符串的结尾来兼容这种⾏为(python2.x中write ⽅法不会换⾏):
# Python 2.x & 3.x compatible
import sys
sys.stdout.write('Hello World!n')
Import your way to a solution
⼀般情况情况下,import时没什么烦恼,只要正确的导⼊就⾏,但在下⾯代码中,我们想导⼊urlopen()函数,在Python2中,他同时存在与urllib2和urllib2中(我们使⽤后者),在Python3中,他被集成到了quest中,⽽你的⽅案是要既能在2.x 和3.x中正常⼯作:
try:
from urllib2 import urlopen
except ImportError:
quest import urlopen
出于对内存的保护,也许你对iterator(Python3)版本的zip()更加有兴趣,在Python2中,iterator版本是itertools.izip()。这个函数在Python3中被重命名替换成了zip()。如果你使⽤迭代版本,导⼊语句也⾮常直⽩:
try:
from itertools import izip as zip
except ImportError:
pass
另⼀个列⼦是看来来并不怎么优雅的StringIO类,在Python2中,纯Python版本是StringIO模块,意味着访问的时候是通过StringIO.StringIO,同样还有⼀个更为快速的C语⾔版本,位于cStringIO.StringIO,不过这取决你的Python安装版本,你可以优先使⽤cStringIO然后是StringIO(如果cStringIO不能⽤的话)。在Python3中,Unicode是默认的string类型,但是如果你做任何和⽹络相关的操作,很有可能你不得不⽤ASCII/字节字符串来操作,所以代替StringIO,你要io.BytesIO,为了达到你想要的,这个导⼊看起来有点丑:
try:
from io import BytesIO as StringIO
except ImportError:
try:
python的类怎么输出printffrom cStringIO import StringIO
except ImportError:
from StringIO import StringIO
Putting it all together
如果你运⽓好的话,上⾯那些就是你要准备做的全部,剩下的代码都⽐开始设置的地⽅更简单。如果你按照上⾯的⽅式导⼊了distutils.log.warn()[printf()],url*urlopen(),*.StringIO和⼀个标准的导⼊:ElementTree(2.5及更新的),现在你就可以写⼀个⾮常简短短的解析器来展⽰从Google News服务中提供的头条故事(译注:当然⾸先得备⼀个梯⼦),只需⼋⾏代码:g = urlopen('le/news?topic=h&output=rss')
f = ad())
g.close()
tree = ElementTree.parse(f)
f.close()
for elmt iterator():
if elmt.tag == 'title' and not
printf('- %s' % )
这段脚本在2.x和3.x下⾯运⾏时,不需要做任何改动,运⾏效果完全⼀样,当然,如果你正在使⽤的是2.4或者更⽼的版本,你需要单独下载ElementTree。
但是有时候感觉这些改变把你优雅的Python代码弄得⼀团糟,毕竟可读性才是最重要的,如果你要优先保证代码的整洁⽽且在不修改任何地⽅的前提下运⾏在两个版本的Python环境中,那么你可以看⼀下six包。
six⼀个兼容库,它的主要任务是提供接⼝隐藏复杂的细节,你可以在到它。⽆论你是使⽤像six这样的库还是⽤⾃⼰的⽅法来做,我们希望这个简短的介绍可以让你开始考虑写的代码能够在2.x和3.x下同时运⾏。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。