写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_types
Possible class types. In Python 2, this encompasses old-style and new-style classes. In Python 3, this is just new-styles.
six.integer_types
Possible integer types. In Python 2, this is
long
andint
, and in Python 3, justint
.
six.string_types
Possible types for text data. This is
basestring()
in Python 2 andstr
in Python 3.
six.text_type
Type for representing (Unicode) textual data. This is
unicode()
in Python 2 andstr
in Python 3.
six.binary_type
Type for representing binary data. This is
str
in Python 2 andbytes
in 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.PY2
A boolean indicating if the code is running on Python 2.
six.PY3
A 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