博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编写兼容 Python 2 和 Python 3 的代码
阅读量:6651 次
发布时间:2019-06-25

本文共 17756 字,大约阅读时间需要 59 分钟。

hot3.png

该笔记向你展示了编写不会过时的兼容 Python 2 和 Python 3 的代码风格。

它是 Ed Schofield 在 PyCon AU 2014 的演讲,“ Writing 2/3 compatible code ”。

最低版本:

  • Python 2: 2.6+
  • Python 3: 3.3+

安装

一些下面的 imports 适用于使用 pip 安装在 PyPI 上安装:

import future        # pip install futureimport past          # pip install pastimport six           # pip install six

以下的脚本也适用于 pip 安装:

futurize             # pip install futurizepasteurize           # pip install pasteurize

查看  和  获取更多消息。

基本语法差异

print

# Python 2 only:print 'Hello'
# Python 2 and 3:print('Hello')

为了打印出多个 strings。 import print_function 来防止 Py2 把它解释成一个元组。

# Python 2 only:print 'Hello', 'Guido'
# Python 2 and 3:from __future__ import print_function    # (at top of module)print('Hello', 'Guido')
# Python 2 only:print >> sys.stderr, 'Hello'
# Python 2 and 3:from __future__ import print_functionprint('Hello', file=sys.stderr)
# Python 2 only:print 'Hello',
# Python 2 and 3:from __future__ import print_functionprint('Hello', end='')

抛出异常

# Python 2 only:raise ValueError, "dodgy value"
# Python 2 and 3:raise ValueError("dodgy value")

使用 traceback 抛出异常:

# Python 2 only:traceback = sys.exc_info()[2]raise ValueError, "dodgy value", traceback
# Python 3 only:raise ValueError("dodgy value").with_traceback()
# Python 2 and 3: option 1from six import reraise as raise_# orfrom future.utils import raise_traceback = sys.exc_info()[2]raise_(ValueError, "dodgy value", traceback)
# Python 2 and 3: option 2from future.utils import raise_with_tracebackraise_with_traceback(ValueError("dodgy value"))

异常链 (PEP 3134):

# Setup:class DatabaseError(Exception):    pass
# Python 3 onlyclass FileDatabase:    def __init__(self, filename):        try:            self.file = open(filename)        except IOError as exc:            raise DatabaseError('failed to open') from exc
# Python 2 and 3:from future.utils import raise_fromclass FileDatabase:    def __init__(self, filename):        try:            self.file = open(filename)        except IOError as exc:            raise_from(DatabaseError('failed to open'), exc)
# Testing the above:try:    fd = FileDatabase('non_existent_file.txt')except Exception as e:    assert isinstance(e.__cause__, IOError)    # FileNotFoundError on Py3.3+ inherits from IOError

捕获异常

# Python 2 only:try:    ...except ValueError, e:    ...
# Python 2 and 3:try:    ...except ValueError as e:    ...

除法

整除(rounding down):

# Python 2 only:assert 2 / 3 == 0
# Python 2 and 3:assert 2 // 3 == 0

“True division” (float division):

# Python 3 only:assert 3 / 2 == 1.5
# Python 2 and 3:from __future__ import division    # (at top of module)assert 3 / 2 == 1.5

“Old division” (i.e. compatible with Py2 behaviour):

# Python 2 only:a = b / c            # with any types
# Python 2 and 3:from past.utils import old_diva = old_div(b, c)    # always same as / on Py2

长整数

短整数在 Python 3 中已经去除了,并且 long 已经变成了 int(没有 L 在 repr 后面)

# Python 2 onlyk = 9223372036854775808L# Python 2 and 3:k = 9223372036854775808
# Python 2 onlybigint = 1L# Python 2 and 3from future.builtins import intbigint = int(1)

为了测试一个值是否是整数(任何类型):

# Python 2 only:if isinstance(x, (int, long)):    ...# Python 3 only:if isinstance(x, int):    ...# Python 2 and 3: option 1from future.builtins import int    # subclass of long on Py2if isinstance(x, int):             # matches both int and long on Py2    ...# Python 2 and 3: option 2from past.builtins import longif isinstance(x, (int, long)):    ...

八进制常量

0644     # Python 2 only
0o644    # Python 2 and 3

Backtick repr

`x`      # Python 2 only
repr(x)  # Python 2 and 3

元类

class BaseForm(object):    passclass FormType(type):    pass
# Python 2 only:class Form(BaseForm):    __metaclass__ = FormType    pass
# Python 3 only:class Form(BaseForm, metaclass=FormType):    pass
# Python 2 and 3:from six import with_metaclass# orfrom future.utils import with_metaclassclass Form(with_metaclass(FormType, BaseForm)):    pass

Strings 和 bytes

Unicode(text) 字符字面量

如果你更新一个已经存在的 Python 2 的代码库,为所有的字符字面量使用 u 做前缀作为 unicode 的标记可能会更好。

# Python 2 onlys1 = 'The Zen of Python's2 = u'きたないのよりきれいな方がいい\n'# Python 2 and 3s1 = u'The Zen of Python's2 = u'きたないのよりきれいな方がいい\n'

futurize 和 python-modernize 工具没有提供一个选项自动完成这个。

如果你为了个项目编写新的代码库,你可以用一个 unicode 字符模块标记所有的字符字面量。

# Python 2 and 3from __future__ import unicode_literals    # at top of modules1 = 'The Zen of Python's2 = 'きたないのよりきれいな方がいい\n'

查看  这里获取更多的关于讨论使用哪种风格的讨论。

字节-字符 字面量

# Python 2 onlys = 'This must be a byte-string'# Python 2 and 3s = b'This must be a byte-string'

To loop over a byte-string with possible high-bit characters, obtaining each character as a byte-string of length 1:

# Python 2 only:for bytechar in 'byte-string with high-bit chars like \xf9':    ...# Python 3 only:for myint in b'byte-string with high-bit chars like \xf9':    bytechar = bytes([myint])# Python 2 and 3:from future.builtins import bytesfor myint in bytes(b'byte-string with high-bit chars like \xf9'):    bytechar = bytes([myint])

chr() 和 .encode('latin-1') 其中的任何一个都可以用于把一个 int 转换成一个 1-char byte string

# Python 3 only:for myint in b'byte-string with high-bit chars like \xf9':    char = chr(myint)    # returns a unicode string    bytechar = char.encode('latin-1')# Python 2 and 3:from future.builtins import bytes, chrfor myint in bytes(b'byte-string with high-bit chars like \xf9'):    char = chr(myint)    # returns a unicode string    bytechar = char.encode('latin-1')    # forces returning a byte str

basestring

# Python 2 only:a = u'abc'b = 'def'assert (isinstance(a, basestring) and isinstance(b, basestring))# Python 2 and 3: alternative 1from past.builtins import basestring    # pip install futurea = u'abc'b = b'def'assert (isinstance(a, basestring) and isinstance(b, basestring))
# Python 2 and 3: alternative 2: refactor the code to avoid considering# byte-strings as strings.from future.builtins import stra = u'abc'b = b'def'c = b.decode()assert isinstance(a, str) and isinstance(c, str)# ...

unicode

# Python 2 only:templates = [u"blog/blog_post_detail_%s.html" % unicode(slug)]
# Python 2 and 3: alternative 1from future.builtins import strtemplates = [u"blog/blog_post_detail_%s.html" % str(slug)]
# Python 2 and 3: alternative 2from future.builtins import str as texttemplates = [u"blog/blog_post_detail_%s.html" % text(slug)]

StringIO

# Python 2 only:from StringIO import StringIO# or:from cStringIO import StringIO# Python 2 and 3:from io import BytesIO     # for handling byte stringsfrom io import StringIO    # for handling unicode strings

Imports relative to a package

假设包的结构是这样的:

mypackage/    __init__.py    submodule1.py    submodule2.py

submodule1.py 的代码如下:

# Python 2 only:import submodule2
# Python 2 and 3:from . import submodule2
# Python 2 and 3:# To make Py2 code safer (more like Py3) by preventing# implicit relative imports, you can also add this to the top:from __future__ import absolute_import

字典

heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}

Iterating through dict keys/values/items

迭代字典的键:

# Python 2 only:for key in heights.iterkeys():    ...
# Python 2 and 3:for key in heights:    ...

迭代字典的值:

# Python 2 only:for value in heights.itervalues():    ...
# Idiomatic Python 3for value in heights.values():    # extra memory overhead on Py2    ...
# Python 2 and 3: option 1from future.builtins import dictheights = dict(Fred=175, Anne=166, Joe=192)for key in heights.values():    # efficient on Py2 and Py3    ...
# Python 2 and 3: option 2from future.builtins import itervalues# orfrom six import itervaluesfor key in itervalues(heights):    ...

迭代字典元素:

# Python 2 only:for (key, value) in heights.iteritems():    ...
# Python 2 and 3: option 1for (key, value) in heights.items():    # inefficient on Py2    ...
# Python 2 and 3: option 2from future.builtins import iteritems# orfrom six import iteritemsfor (key, value) in iteritems(heights):    ...

字典的键/值/元素 作为一个列表

字典的键作为一个列表:

# Python 2 only:keylist = heights.keys()assert isinstance(keylist, list)
# Python 2 and 3:keylist = list(heights)assert isinstance(keylist, list)

字典的值作为一个列表:

# Python 2 only:heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}valuelist = heights.values()assert isinstance(valuelist, list)
# Python 2 and 3: option 1valuelist = list(heights.values())    # inefficient on Py2
# Python 2 and 3: option 2from future.builtins import dictheights = dict(Fred=175, Anne=166, Joe=192)valuelist = list(heights.values())
# Python 2 and 3: option 3from future.utils import listvaluesvaluelist = listvalues(heights)
# Python 2 and 3: option 4from future.utils import itervalues# orfrom six import itervaluesvaluelist = list(itervalues(heights))

字典元素作为一个列表:

# Python 2 and 3: option 1itemlist = list(heights.items())    # inefficient on Py2
# Python 2 and 3: option 2from future.utils import listitemsitemlist = listitems(heights)
# Python 2 and 3: option 3from future.utils import iteritems# orfrom six import iteritemsitemlist = list(iteritems(heights))

自定义类的行为

自定义迭代器

# Python 2 onlyclass Upper(object):    def __init__(self, iterable):        self._iter = iter(iterable)    def next(self):          # Py2-style        return self._iter.next().upper()    def __iter__(self):        return selfitr = Upper('hello')assert itr.next() == 'H'     # Py2-styleassert list(itr) == list('ELLO')
# Python 2 and 3: option 1from future.builtins import objectclass Upper(object):    def __init__(self, iterable):        self._iter = iter(iterable)    def __next__(self):      # Py3-style iterator interface        return next(self._iter).upper()  # builtin next() function calls    def __iter__(self):        return selfitr = Upper('hello')assert next(itr) == 'H'      # compatible styleassert list(itr) == list('ELLO')
# Python 2 and 3: option 2from future.utils import implements_iterator@implements_iteratorclass Upper(object):    def __init__(self, iterable):        self._iter = iter(iterable)    def __next__(self):                  # Py3-style iterator interface        return next(self._iter).upper()  # builtin next() function calls    def __iter__(self):        return selfitr = Upper('hello')assert next(itr) == 'H'assert list(itr) == list('ELLO')

自定义 __str__ 方法

# Python 2 only:class MyClass(object):    def __unicode__(self):        return 'Unicode string: \u5b54\u5b50'    def __str__(self):        return unicode(self).encode('utf-8')a = MyClass()print(a)    # prints encoded string
# Python 2 and 3:from future.utils import python_2_unicode_compatible@python_2_unicode_compatibleclass MyClass(object):    def __str__(self):        return u'Unicode string: \u5b54\u5b50'a = MyClass()print(a)    # prints string encoded as utf-8 on Py2
Unicode string: 孔子

自定义 __nonzero__ 对比 __bool__ 方法

# Python 2 only:class AllOrNothing(object):    def __init__(self, l):        self.l = l    def __nonzero__(self):        return all(self.l)container = AllOrNothing([0, 100, 200])assert not bool(container)
# Python 2 and 3:from future.builtins import objectclass AllOrNothing(object):    def __init__(self, l):        self.l = l    def __bool__(self):        return all(self.l)container = AllOrNothing([0, 100, 200])assert not bool(container)

列表对迭代器

xrange

# Python 2 only:for i in xrange(10**8):    ...
# Python 2 and 3: forward-compatiblefrom future.builtins import rangefor i in range(10**8):    ...
# Python 2 and 3: backward-compatiblefrom past.builtins import xrangefor i in xrange(10**8):    ...

range

# Python 2 onlymylist = range(5)assert mylist == [0, 1, 2, 3, 4]
# Python 2 and 3: forward-compatible: option 1mylist = list(range(5))            # copies memory on Py2assert mylist == [0, 1, 2, 3, 4]
# Python 2 and 3: forward-compatible: option 2from future.builtins import rangemylist = list(range(5))assert mylist == [0, 1, 2, 3, 4]
# Python 2 and 3: option 3from future.utils import lrangemylist = lrange(5)assert mylist == [0, 1, 2, 3, 4]
# Python 2 and 3: backward compatiblefrom past.builtins import rangemylist = range(5)assert mylist == [0, 1, 2, 3, 4]

map

# Python 2 only:mynewlist = map(f, myoldlist)assert mynewlist == [f(x) for x in myoldlist]
# Python 2 and 3: option 1# Idiomatic Py3, but inefficient on Py2mynewlist = list(map(f, myoldlist))assert mynewlist == [f(x) for x in myoldlist]
# Python 2 and 3: option 2from future.builtins import mapmynewlist = list(map(f, myoldlist))assert mynewlist == [f(x) for x in myoldlist]
# Python 2 and 3: option 3try:    import itertools.imap as mapexcept ImportError:    passmynewlist = list(map(f, myoldlist))    # inefficient on Py2assert mynewlist == [f(x) for x in myoldlist]
# Python 2 and 3: option 4from future.utils import lmapmynewlist = lmap(f, myoldlist)assert mynewlist == [f(x) for x in myoldlist]
# Python 2 and 3: option 5from past.builtins import mapmynewlist = map(f, myoldlist)assert mynewlist == [f(x) for x in myoldlist]

imap

# Python 2 only:from itertools import imapmyiter = imap(func, myoldlist)assert isinstance(myiter, iter)
# Python 3 only:myiter = map(func, myoldlist)assert isinstance(myiter, iter)
# Python 2 and 3: option 1from future.builtins import mapmyiter = map(func, myoldlist)assert isinstance(myiter, iter)
# Python 2 and 3: option 2try:    import itertools.imap as mapexcept ImportError:    passmyiter = map(func, myoldlist)assert isinstance(myiter, iter)

zip, izip

As above with zip and itertools.izip.

filter, ifilter

As above with filter and itertools.ifilter too

其他内建函数

File IO with open()

# Python 2 onlyf = open('myfile.txt')data = f.read()              # as a byte stringtext = data.decode('utf-8')# Python 2 and 3: alternative 1from io import openf = open('myfile.txt', 'rb')data = f.read()              # as bytestext = data.decode('utf-8')  # unicode, not bytes# Python 2 and 3: alternative 2from io import openf = open('myfile.txt', encoding='utf-8')text = f.read()    # unicode, not bytes

reduce()

# Python 2 only:assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
# Python 2 and 3:from functools import reduceassert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5

raw_input()

# Python 2 only:name = raw_input('What is your name? ')assert isinstance(name, str)    # native str
# Python 2 and 3:from future.builtins import inputname = input('What is your name? ')assert isinstance(name, str)    # native str on Py2 and Py3

input()

# Python 2 only:input("Type something safe please: ")
# Python 2 and 3from future.builtins import inputeval(input("Type something safe please: "))

警告:使用上面任何一个都是不安全的

file()

# Python 2 only:f = file(pathname)
# Python 2 and 3:f = open(pathname)# But preferably, use this:from io import openf = open(pathname, 'rb')   # if f.read() should return bytes# orf = open(pathname, 'rt')   # if f.read() should return unicode text

execfile()

# Python 2 only:execfile('myfile.py')
# Python 2 and 3: alternative 1from past.builtins import execfileexecfile('myfile.py')
# Python 2 and 3: alternative 2exec(compile(open('myfile.py').read()))# This can sometimes cause this:#     SyntaxError: function ... uses import * and bare exec ...# See https://github.com/PythonCharmers/python-future/issues/37

unichr()

# Python 2 only:assert unichr(8364) == '€'
# Python 3 only:assert chr(8364) == '€'
# Python 2 and 3:from future.builtins import chrassert chr(8364) == '€'

intern()

# Python 2 only:intern('mystring')
# Python 3 only:from sys import internintern('mystring')
# Python 2 and 3: alternative 1from past.builtins import internintern('mystring')
# Python 2 and 3: alternative 2try:    from sys import internexcept ImportError:    passintern('mystring')

apply()

args = ('a', 'b')kwargs = {'kwarg1': True}
# Python 2 only:apply(f, args, kwargs)
# Python 2 and 3: alternative 1f(*args, **kwargs)
# Python 2 and 3: alternative 2from past.builtins import applyapply(f, args, kwargs)

chr()

# Python 2 only:assert chr(64) == b'@'assert chr(200) == b'\xc8'
# Python 3 only: option 1assert chr(64).encode('latin-1') == b'@'assert chr(0xc8).encode('latin-1') == b'\xc8'
# Python 2 and 3: option 1from future.builtins import chrassert chr(64).encode('latin-1') == b'@'assert chr(0xc8).encode('latin-1') == b'\xc8'
# Python 3 only: option 2assert bytes([64]) == b'@'assert bytes([0xc8]) == b'\xc8'
# Python 2 and 3: option 2from future.builtins import bytesassert bytes([64]) == b'@'assert bytes([0xc8]) == b'\xc8'

cmp()

# Python 2 only:assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
# Python 2 and 3: alternative 1from past.builtins import cmpassert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
# Python 2 and 3: alternative 2cmp = lambda(x, y): (x > y) - (x < y)assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0

reload()

# Python 2 only:reload(mymodule)
# Python 2 and 3from imp import reloadreload(mymodule)

标准库

StringIO 模块

# Python 2 onlyfrom StringIO import StringIOfrom cStringIO import StringIO
# Python 2 and 3from io import BytesIO# and refactor StringIO() calls to BytesIO() if passing byte-strings

http 模块

# Python 2 only:import httplibimport Cookieimport cookielibimport BaseHTTPServerimport SimpleHTTPServerimport CGIHttpServer# Python 2 and 3:from future.standard_library import hookswith hooks():    import http.client    import http.cookies    import http.cookiejar    import http.server

urllib 模块

这个使用 urllib 的示例,相同的模式也适用于其他的已经移除的模块(一个完整的列表  )

# Python 2 only:from urlparse import urlparsefrom urllib import urlencode
# Python 3 only:from urllib.parse import urlparse, urlencode
# Python 2 and 3: alternative 1from future.standard_library import hookswith hooks():    from urllib.parse import urlparse, urlencode
# Python 2 and 3: alternative 2from future.moves.urllib.parse import urlparse, urlencode# orfrom six.moves.urllib.parse import urlparse, urlencode
# Python 2 and 3: alternative 3try:    from urllib.parse import urlparse, urlencodeexcept ImportError:    from urlparse import urlparse    from urllib import urlencode

转载于:https://my.oschina.net/u/3346994/blog/919464

你可能感兴趣的文章
修改10g自动统计信息收集作业GATHER_STATS_JOB到仅仅周末执行
查看>>
Calibrate测试Exadata IO
查看>>
【C语言】15-预处理指令1-宏定义
查看>>
【C语言】19-static和extern关键字1-对函数的作用
查看>>
9、单机运行环境搭建之 --CentOS-6.4下mysqldump 备份与还原数据库
查看>>
分享:C++中头文件、源文件之间的区别与联系
查看>>
好类 笔记
查看>>
Web前端浏览器兼容初探【转】
查看>>
菜鸟开技术博啦
查看>>
关于多线程生命周期原理
查看>>
如何使用U盘安装操作系统 安装GHOST XP, xp纯净版
查看>>
POJ 1062 昂贵的聘礼
查看>>
理解Java对象序列化——Serializable接口
查看>>
一个简易的WebServer程序
查看>>
Python学习入门基础教程(learning Python)--5.3 Python写文件基础
查看>>
关于js加密解密
查看>>
JBoss7快速入门
查看>>
Sequence one(hdu2610dfs+去重)
查看>>
每日英语:Rethinking How We Watch TV
查看>>
[置顶] EasyMock的简单使用
查看>>