编写同时兼容Python2.x与Python3.x版本的代码的几个示例


Posted in Python onMarch 30, 2015

编写兼容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中,他被集成到了urllib.request中,而你的方案是要既能在2.x和3.x中正常工作:

try:
 from urllib2 import urlopen
except ImportError:
 from urllib.request 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:
  from cStringIO import StringIO
 except ImportError:
  from StringIO import StringIO

Putting it all together

如果你运气好的话,上面那些就是你要准备做的全部,剩下的代码都比开始设置的地方更简单。如果你按照上面的方式导入了distutils.log.warn()[printf()],url*urlopen(),*.StringIO和一个标准的导入:xml.etree.ElementTree(2.5及更新的),现在你就可以写一个非常简短短的解析器来展示从Google News服务中提供的头条故事(译注:当然首先得备一个梯子),只需八行代码:
 

g = urlopen('http://news.google.com/news?topic=h&output=rss')
f = StringIO(g.read())
g.close()
tree = xml.etree.ElementTree.parse(f)
f.close()
for elmt in tree.getiterator():
 if elmt.tag == 'title' and not
   elmt.text.startswith('Top Stories'):
  printf('- %s' % elmt.text)

这段脚本在2.x和3.x下面运行时,不需要做任何改动,运行效果完全一样,当然,如果你正在使用的是2.4或者更老的版本,你需要单独下载ElementTree。

但是有时候感觉这些改变把你优雅的Python代码弄得一团糟,毕竟可读性才是最重要的,如果你要优先保证代码的整洁而且在不修改任何地方的前提下运行在两个版本的Python环境中,那么你可以看一下six包。

six一个兼容库,它的主要任务是提供接口隐藏复杂的细节,你可以在这里找到它。无论你是使用像six这样的库还是用自己的方法来做,我们希望这个简短的介绍可以让你开始考虑写的代码能够在2.x和3.x下同时运行。

Python 相关文章推荐
Python列表生成器的循环技巧分享
Mar 06 Python
[原创]使用豆瓣提供的国内pypi源
Jul 02 Python
Python单体模式的几种常见实现方法详解
Jul 28 Python
详解 Python 读写XML文件的实例
Aug 02 Python
Python+selenium实现自动循环扔QQ邮箱漂流瓶
May 29 Python
使用Python自动化破解自定义字体混淆信息的方法实例
Feb 13 Python
详解Pandas之容易让人混淆的行选择和列选择
Jul 10 Python
python 实现识别图片上的数字
Jul 30 Python
Python中函数的返回值示例浅析
Aug 28 Python
Python二维数组实现求出3*3矩阵对角线元素的和示例
Nov 29 Python
python 装饰器的使用示例
Oct 10 Python
python 实现逻辑回归
Dec 30 Python
以Python的Pyspider为例剖析搜索引擎的网络爬虫实现方法
Mar 30 #Python
在树莓派2或树莓派B+上安装Python和OpenCV的教程
Mar 30 #Python
Python中利用函数装饰器实现备忘功能
Mar 30 #Python
利用Python绘制MySQL数据图实现数据可视化
Mar 30 #Python
Python面向对象编程中的类和对象学习教程
Mar 30 #Python
详细介绍Python函数中的默认参数
Mar 30 #Python
在Python中利用Into包整洁地进行数据迁移的教程
Mar 30 #Python
You might like
php数组函数序列之rsort() - 对数组的元素值进行降序排序
2011/11/02 PHP
php中return的用法实例分析
2015/02/28 PHP
PHP实现发送微博消息功能完整示例
2019/12/04 PHP
php生成随机数/生成随机字符串的方法小结【5种方法】
2020/05/27 PHP
Mootools 图片展示插件(lightbox,ImageMenu)收集集合
2010/05/21 Javascript
基于jquery完美拖拽,可返回拖动轨迹
2012/03/29 Javascript
HTML,CSS,JavaScript速查表推荐
2014/12/02 Javascript
javascript解三阶幻方(九宫格)
2015/04/22 Javascript
js如何打印object对象
2015/10/16 Javascript
js实现(全选)多选按钮的方法【附实例】
2016/03/30 Javascript
jQuery ajax应用总结
2016/06/02 Javascript
javascript动画之模拟拖拽效果篇
2016/09/26 Javascript
动态加载css方法实现和深入解析
2017/01/18 Javascript
js 转义字符及URI编码详解
2017/02/28 Javascript
jQuery+PHP+Mysql实现抽奖程序
2020/04/12 jQuery
详解js中的原型,原型对象,原型链
2020/07/16 Javascript
vue3.0+vue-router+element-plus初实践
2020/12/02 Vue.js
[53:13]2014 DOTA2国际邀请赛中国区预选赛5.21 DT VS LGD-GAMING
2014/05/22 DOTA
[01:00]选手抵达华西村 整装待发备战2016国际邀请赛中国区预选赛
2016/06/25 DOTA
[42:11]TNC vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
Python的装饰器使用详解
2017/06/26 Python
对python 各种删除文件失败的处理方式分享
2018/04/24 Python
浅析Python装饰器以及装饰器模式
2018/05/28 Python
解决vscode python print 输出窗口中文乱码的问题
2018/12/03 Python
python matplotlib实现双Y轴的实例
2019/02/12 Python
使用selenium和pyquery爬取京东商品列表过程解析
2019/08/15 Python
Python 2种方法求某个范围内的所有素数(质数)
2020/01/31 Python
CSS3实现DIV圆角效果完整代码
2012/10/10 HTML / CSS
RetroStage德国:复古服装
2019/02/03 全球购物
客服主管岗位职责
2013/12/13 职场文书
2014年骨干教师工作总结
2014/12/19 职场文书
与死神共舞观后感
2015/06/15 职场文书
2016年“世界环境日”校园广播稿
2015/12/18 职场文书
应届生个人的求职(自荐信范文2篇)
2019/08/23 职场文书
python中使用 unittest.TestCase单元测试的用例详解
2021/08/30 Python
python基础之模块的导入
2021/10/24 Python