写py2、py3兼容的代码
文章目录
写py2、py3兼容的代码
用到一段时间python,之前也重点复习了一次python3。但工作中运行环境是python2.7,于是要求写出py2、py3都兼容的代码。下面将涉及到的几点技巧列举出来以备忘。
print函数
py3中print语句没有了,取而代之的是print()函数。 Python 2.6与Python 2.7部分地支持这种形式的print语法。因此保险起见,新写的代码都使用print函数。
from __future__ import print_function
print("fish", "panda", sep=', ')
Unicode
Python 2 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。
现在, 在 Python 3,我们最终有了 Unicode (utf-8) 字符串,以及一个字节类:byte 和 bytearrays。
由于 Python3.X 源码文件默认使用utf-8编码,这就使得以下代码是合法的:
>>> 中国 = 'china'
>>>print(中国)
china
Python 2.x
>>> str = "我爱北京天安门"
>>> str
'\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8'
>>> str = u"我爱北京天安门"
>>> str
u'\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8'
Python 3.x
>>> str = "我爱北京天安门"
>>> str
'我爱北京天安门'
个人还是喜欢py3的这种明确两种不同类型的方案,因此新写的代码都使用以下方案。
from __future__ import unicode_literals
txt='中国'
>>> txt
u'\u4e2d\u56fd'
>>> type(txt)
<type 'unicode'>
>>> print(txt)
中国
arr=b'abcd'
>>> arr
'abcd'
>>> type(arr)
<type 'str'>
>>> print(arr)
abcd
除法运算
在python 2.x中/除法就跟我们熟悉的大多数语言,比如Java啊C啊差不多,整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。
在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。
而对于//除法,这种除法叫做floor除法,会对除法的结果自动进行一个floor操作,在python 2.x和python 3.x中是一致的。注意的是floor除法并不是舍弃小数部分,而是执行floor操作,如果要截取小数部分,那么需要使用math模块的trunc函数。
个人还是喜欢py3这种方案,毕竟是从java转过来的,因此新定的代码都使用以下方案。
from __future__ import division
>>> 1/2
0.5
>>> 1//2
0
>>> trunc(1/2)
0
>>> -1//2
-1
>>> trunc(-1/2)
0
异常
在 Python 3 中处理异常也轻微的改变了,在 Python 3 中我们现在使用 as 作为关键词。
捕获异常的语法由 except exc, var 改为 except exc as var。
使用语法except (exc1, exc2) as var可以同时捕获多种类别的异常。 Python 2.6已经支持这两种语法。
- 在2.x时代,所有类型的对象都是可以被直接抛出的,在3.x时代,只有继承自BaseException的对象才可以被抛出。
- 2.x raise语句使用逗号将抛出对象类型和参数分开,3.x取消了这种奇葩的写法,直接调用构造函数抛出对象即可。
这里倒没有异议了,本来就常见原来py2那种奇葩写法很奇怪,只使用py3的写法就可以了。
try:
raise BaseException('fdf')
except BaseException as err:
print(err)
八进制字面量表示
八进制数必须写成0o777,原来的形式0777不能用了;二进制必须写成0b111。
新增了一个bin()函数用于将一个整数转换成二进制字串。 Python 2.6已经支持这两种语法。
在Python 3.x中,表示八进制字面量的方式只有一种,就是0o1000。
很简单,只使用py3支持的写法。
不等运算符
Python 2.x中不等于有两种写法 != 和 <>。
Python 3.x中去掉了<>, 只有!=一种写法,还好,我从来没有使用<>的习惯。
数据类型
- Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
- 新增了bytes类型,对应于2.X版本的八位串
这里如果要进行类型判断,优先使用six模块提供的兼容功能。
six.class_typesPossible class types. In Python 2, this encompasses old-style and new-style classes. In Python 3, this is just new-styles.
six.integer_typesPossible integer types. In Python 2, this is
longandint, and in Python 3, justint.
six.string_typesPossible types for text data. This is
basestring()in Python 2 andstrin Python 3.
six.text_typeType for representing (Unicode) textual data. This is
unicode()in Python 2 andstrin Python 3.
six.binary_typeType for representing binary data. This is
strin Python 2 andbytesin Python 3.
import six
def dispatch_types(value):
if isinstance(value, six.integer_types):
handle_integer(value)
elif isinstance(value, six.class_types):
handle_class(value)
elif isinstance(value, six.string_types):
handle_string(value)
dict的相关方法调整
dict的.keys()、.items 和.values()方法返回迭代器,而之前的iterkeys()等函数都被废弃。同时去掉的还有 dict.has_key(),用 in替代它吧。
这里还是使用six模块提供的兼容功能。
six.iterkeys(dictionary, **kwargs)Returns an iterator over dictionary‘s keys. This replaces
dictionary.iterkeys()on Python 2 anddictionary.keys()on Python 3. kwargs are passed through to the underlying method.
six.itervalues(dictionary, **kwargs)Returns an iterator over dictionary‘s values. This replaces
dictionary.itervalues()on Python 2 anddictionary.values()on Python 3. kwargs are passed through to the underlying method.
six.iteritems(dictionary, **kwargs)Returns an iterator over dictionary‘s items. This replaces
dictionary.iteritems()on Python 2 anddictionary.items()on Python 3. kwargs are passed through to the underlying method.
six.iterlists(dictionary, **kwargs)Calls
dictionary.iterlists()on Python 2 anddictionary.lists()on Python 3. No builtin Python mapping type has such a method; this method is intended for use with multi-valued dictionaries like Werkzeug’s.kwargs are passed through to the underlying method.
six.viewkeys(dictionary)Return a view over dictionary‘s keys. This replaces
dict.viewkeys()on Python 2.7 anddict.keys()on Python 3.
six.viewvalues(dictionary)Return a view over dictionary‘s values. This replaces
dict.viewvalues()on Python 2.7 anddict.values()on Python 3.
six.viewitems(dictionary)Return a view over dictionary‘s items. This replaces
dict.viewitems()on Python 2.7 anddict.items()on Python 3.
标准库及函数名称变更
py3重新组织了一些标准库及一些函数,为了保证在py2、py3下代码都工作正常,这里使用six模块提供的兼容功能。
from six.moves.cPickle import loads
Supported renames:
| Name | Python 2 name | Python 3 name |
|---|---|---|
builtins | __builtin__ | builtins |
configparser | ConfigParser | configparser |
copyreg | copy_reg | copyreg |
cPickle | cPickle | pickle |
cStringIO | cStringIO.StringIO() | io.StringIO |
dbm_gnu | gdbm | dbm.gnu |
_dummy_thread | dummy_thread | _dummy_thread |
email_mime_multipart | email.MIMEMultipart | email.mime.multipart |
email_mime_nonmultipart | email.MIMENonMultipart | email.mime.nonmultipart |
email_mime_text | email.MIMEText | email.mime.text |
email_mime_base | email.MIMEBase | email.mime.base |
filter | itertools.ifilter() | filter() |
filterfalse | itertools.ifilterfalse() | itertools.filterfalse() |
getcwd | os.getcwdu() | os.getcwd() |
getcwdb | os.getcwd() | os.getcwdb() |
http_cookiejar | cookielib | http.cookiejar |
http_cookies | Cookie | http.cookies |
html_entities | htmlentitydefs | html.entities |
html_parser | HTMLParser | html.parser |
http_client | httplib | http.client |
BaseHTTPServer | BaseHTTPServer | http.server |
CGIHTTPServer | CGIHTTPServer | http.server |
SimpleHTTPServer | SimpleHTTPServer | http.server |
input | raw_input() | input() |
intern | intern() | sys.intern() |
map | itertools.imap() | map() |
queue | Queue | queue |
range | xrange() | range |
reduce | reduce() | functools.reduce() |
reload_module | reload() | imp.reload(), importlib.reload() on Python 3.4+ |
reprlib | repr | reprlib |
shlex_quote | pipes.quote | shlex.quote |
socketserver | SocketServer | socketserver |
_thread | thread | _thread |
tkinter | Tkinter | tkinter |
tkinter_dialog | Dialog | tkinter.dialog |
tkinter_filedialog | FileDialog | tkinter.FileDialog |
tkinter_scrolledtext | ScrolledText | tkinter.scrolledtext |
tkinter_simpledialog | SimpleDialog | tkinter.simpledialog |
tkinter_ttk | ttk | tkinter.ttk |
tkinter_tix | Tix | tkinter.tix |
tkinter_constants | Tkconstants | tkinter.constants |
tkinter_dnd | Tkdnd | tkinter.dnd |
tkinter_colorchooser | tkColorChooser | tkinter.colorchooser |
tkinter_commondialog | tkCommonDialog | tkinter.commondialog |
tkinter_tkfiledialog | tkFileDialog | tkinter.filedialog |
tkinter_font | tkFont | tkinter.font |
tkinter_messagebox | tkMessageBox | tkinter.messagebox |
tkinter_tksimpledialog | tkSimpleDialog | tkinter.simpledialog |
urllib.parse | See six.moves.urllib.parse | urllib.parse |
urllib.error | See six.moves.urllib.error | urllib.error |
urllib.request | See six.moves.urllib.request | urllib.request |
urllib.response | See six.moves.urllib.response | urllib.response |
urllib.robotparser | robotparser | urllib.robotparser |
urllib_robotparser | robotparser | urllib.robotparser |
UserDict | UserDict.UserDict | collections.UserDict |
UserList | UserList.UserList | collections.UserList |
UserString | UserString.UserString | collections.UserString |
winreg | _winreg | winreg |
xmlrpc_client | xmlrpclib | xmlrpc.client |
xmlrpc_server | SimpleXMLRPCServer | xmlrpc.server |
xrange | xrange() | range |
zip | itertools.izip() | zip() |
zip_longest | itertools.izip_longest() | itertools.zip_longest() |
这里用得比较多的是:
import six.moves.configparser
import six.moves.cPickle
import six.moves.cStringIO
import six.moves.filter
import six.moves.filterfalse
import six.moves.getcwd
import six.moves.http_cookies
import six.moves.html_entities
import six.moves.html_parser
import six.moves.http_client
import six.moves.BaseHTTPServer
import six.moves.CGIHTTPServer
import six.moves.SimpleHTTPServer
import six.moves.input
import six.moves.map
import six.moves.queue
import six.moves.range
import six.moves.reduce
import six.moves.socketserver
import six.moves.zip
import six.moves.zip_longest
import six.moves.urllib.parse
import six.moves.urllib.error
import six.moves.urllib.request
import six.moves.urllib.response
只有按这个方案导入其它模块,即可保证在py2、py3下都可正确导入模块,详细可参看six模块的文档。
版本指示变量
最后如果在py2、py3下逻辑不一致,可使用版本指示变量。
six.PY2A boolean indicating if the code is running on Python 2.
six.PY3A boolean indicating if the code is running on Python 3.
import six
if six.PY2:
# do some thing
pass
elif six.PY3:
# do other thing
pass
文章作者 徐新杰
上次更新 2017-11-05
许可协议 © Copyright 2020 Jeremy Xu