python2/3的兼容性代码编写

前言

本文主要是讲述python 2/3的兼容性代码编写,涉及到python中常用的方法,涵盖方面比较全。

推荐资料:http://python-future.org/compatible_idioms.html。

相关兼容包

python 2/3兼容包主要有两个 futuresix,我们主要是使用 future;两个包都可以通过 pip 安装。

pip install future
pip install six

基本语法差异

print

python3中修改了print写法,将print定义为了一个方法,不再支持'print+空格'的写法,全部统一成了'print()',输出上也有差异,详见代码。

python3: print(value, ..., sep=' ', end='\n', file=sys.stdout)

  • 打印单个值的情况
# Python 2 only: 
In>> print 'Hello'
Out>> Hello

# Python 2 and 3,引入future后老的方法会报错:
In>> from __future__ import print_function
In>> print 'Hello'
Out>> File "<stdin>", line 1
    print 'Hello'
                ^
SyntaxError: invalid syntax
In>> print('Hello')  # 只支持这一种写法
Out>> Hello
  • 打印多个值的情况
# Python 2 only: 打印多个值的时候支持两种写法,输出上也不同
In>> print 'Hello', 'Guido'
Out>> Hello Guido
In>> print('Hello', 'Guido')  # 打印出来是tuple式样
Out>> ('Hello', 'Guido')

# Python 2 and 3:
In>> from __future__ import print_function
In>> print('Hello', 'Guido')
Out>> Hello Guido
  • 选择输出流写法改变
# Python 2 only:
In>> print >> sys.stderr, 'Hello'
Out>> Hello

# Python 2 and 3:
In>> from __future__ import print_function
In>> print('Hello', file=sys.stderr)
Out>> Hello
  • 定义结尾方式不同
# Python 2 only: 指定以逗号结尾
In>> print 'Hello',
Out>> HelloIn>>

# Python 2 and 3:
In>> from __future__ import print_function
In>> print('Hello', end='')
Out>> HelloIn>>

异常处理

  • raise

python3只支持一种写法,类似 raise Exception, "error" 的写法需要修改,详细看代码

# Python 2 only:
In>> raise ValueError, "dodgy value"
Out>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: dodgy value
# Python 3 only:
In>> raise ValueError("dodgy value")
Out>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: dodgy value

In>> raise ValueError, "dodgy value"  # 在python3中,这个写是错误的
Out>> File "<stdin>", line 1
    raise ValueError, "dodgy value"
                    ^
SyntaxError: invalid syntax
# Python 2 and 3:
In>> raise ValueError("dodgy value")
Out>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: dodgy value 
  • 使用traceback对象来溯源错误

traceback对象通常通过sys.exc_info()来获取,但在使用traceback时python2 和 python3写法不同

# Python 2 only:
In>> traceback = sys.exc_info()[2]
In>> raise ValueError, "dodgy value", traceback
# python 3 only:
In>> traceback = sys.exc_info()[2]
In>> raise ValueError("dodgy value").with_traceback(traceback)

兼容性写法有两种:
. 使用future包中的reise_ 或 six包中的reraise

from future.utils import raise_    # 推荐用future,我们已经安装
# or
from six import reraise as raise_

In>> traceback = sys.exc_info()[2]
In>> raise_(ValueError, "dodgy value", traceback)
  1. 使用future包中的raise_with_traceback
from future.utils import raise_with_traceback

In>> raise_with_traceback(ValueError("dodgy value"))
  • python3中增加了raise/from方法,表示异常之间的链式关系

举例说明一下:

# Python 3 only:
In>> def raise_error(): 
In>>     try: 
In>>         1/0 
In>>     except Exception as e: 
In>>         raise ValueError('error value') 

In>> raise_error()
Out>> ---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-20-5896fe6a4509> in raise_error()
      2     try:
----> 3         1/0
      4     except Exception as e:

ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-21-9bc4b0c7c214> in <module>
----> 1 raise_error()

<ipython-input-20-5896fe6a4509> in raise_error()
      3         1/0
      4     except Exception as e:
----> 5         raise ValueError('error value')
      6 

ValueError: error value
# python 3 only:
In>> def raise_error(): 
In>>     try: 
In>>         1/0 
In>>     except Exception as e: 
In>>         raise ValueError('error value') from e  # 这里不同

In>> raise_error()
Out>> ---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-22-95b2c8d2a696> in raise_error()
      2     try:
----> 3         1/0
      4     except Exception as e:

ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
<ipython-input-23-9bc4b0c7c214> in <module>
----> 1 raise_error()

<ipython-input-22-95b2c8d2a696> in raise_error()
      3         1/0
      4     except Exception as e:
----> 5         raise ValueError('error value') from e
      6 

ValueError: error value

这里的不同表现在错误信息中:

  • raise 返回:During handling of the above exception, another exception occurred: 表示 在处理上面的异常时,发生了另外一个异常
  • raise/from 返回:The above exception was the direct cause of the following exception: 表示 上面的异常是造成下面异常的直接原因

兼容性写法:

# Python 2 and 3:
from future.utils import raise_from

In>> def raise_error(): 
In>>     try: 
In>>         1/0 
In>>     except Exception as e: 
In>>         raise ValueError('error value') 

注: python2的环境还没有达到python3中的效果

  • 捕获异常python3中写法修改,python2中需要做兼容
# Python 2 only:
try:
    ...
except ValueError, e:
    ...
# Python 2 and 3:
try:
    ...
except ValueError as e:
    ...

除法运算

python3中除法运算更加精确,除法结果返回浮点数,更符合运算习惯;python2中整数相除返回整数,需要做兼容性修改。

# python 2 only:
In>> 2 / 3
Out>> 0
In>> 3 / 2
Out>> 1
  1. 利用取模来兼容python2的运算。
# python 2 and 3:
In>> from __future__ import division
In>> 2 / 3
Out>> 0.6666666666666666
In>> 3 / 2
Out>> 1.5
In>> 2 // 3  # 利用取模来兼容python2
Out>> 0
In>> 3 // 2
Out>> 1
  1. old_div 方法来兼容python2运算
# Python 2 and 3:
In>> from past.utils import old_div
In>> old_div(3, 2)  # 和python2中的算法是一致的
Out>> 1
In>> old_div(3.0, 2)
Out>> 1.5

整型数据

python3中已经没有长整型数据,long关键字已经删除,long和int都变成了int;即:后缀L已经不存在了。

# Python 2:
In>> 9223372036854775808
Out>> 9223372036854775808L
# python 3:
# 添加后缀L会报错
In>> 9223372036854775808
Out>> 9223372036854775808
In>> 1L
Out>>   File "<stdin>", line 1
    1L
     ^
SyntaxError: invalid syntax
# python 2 and 3:
In>> from builtins import int
In>> int(9223372036854775808)
Out>> 9223372036854775808     # 后缀L不存在了
In>> type(int(9223372036854775808))
Out>> future.types.newint.newint

由于关键字变了,利用isinstance判断一个变量是不是整数的时候需要做兼容。

# Python 2 only:
if isinstance(x, (int, long)):
    pass

# Python 3 only:
if isinstance(x, int):  # 已经没有long关键字了
    pass

有两种方式来做兼容

  1. 利用builtins包中的int,使用新的int类,删除long关键字
# Python 2 and 3:
from builtins import int

if isinstance(x, int):     # 可以匹配python2中的int和long
    pass
  1. 利用past包中的long,保留long关键字
# Python 2 and 3:
from past.builtins import long

if isinstance(x, (int, long)):
    pass

repr函数

python3中删除了反引号操作符(``),python2中的反引号操作符需要改成repr()

# python 2:
In>> x = 1
In>> `x`
Out>> '1'

# Python 2 and 3:
In>> repr(x)
Out>> '1'

Metaclasses

元类引用方式变化

class BaseForm(object):
    pass

class FormType(type):
    pass
# Python 2 only:
class Form(BaseForm):
    __metaclass__ = FormType
    pass
# Python 3 only:
class Form(BaseForm, metaclass=FormType):
    pass

兼容写法可以用future包或six包中的with_metaclass

# Python 2 and 3:
from future.utils import with_metaclass
# or
from six import with_metaclass

class Form(with_metaclass(FormType, BaseForm)):
    pass

string 和 bytes

unicode字符串

python3改变了str的定义,str类型数据表示unicode,不再表示字节符,在中文字符串编写需要注意。python2中的unicode都应加上前缀 u ,明确表示unicode。

获取中文字符串的二进制只能用encode()

# python2:
In>> '我爱中国'
Out>> '\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
In>> u'我爱中国'
Out>> u'\u6211\u7231\u4e2d\u56fd'
# python3:
In>> '我爱中国'
Out>> '我爱中国'
In>> u'我爱中国'
Out>> '我爱中国'
In>> '我爱中国'.encode()
Out>> b'\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'

如果要在新的代码中体现python3的特性,可以引入unicode_literals

# Python 2 and 3
In>> from __future__ import unicode_literals

In>> '我爱中国'
Out>> u'\u6211\u7231\u4e2d\u56fd'    # python2的环境,可以看到输出的是unicode

字节符byte

python3中用 b 前缀赋值只支持ASCII表中的字符,汉字不可以直接用 b 前缀命名得到字节符

# Python 2 
In>> b'我爱中国'
Out>> '\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'
# python 3
In>> b'我爱中国'
Out>> File "<ipython-input-148-fe4e1344c779>", line 1
    b'我爱中国'
           ^
SyntaxError: bytes can only contain ASCII literal characters.

In>> '我爱中国'.encode()
Out>> b'\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd'

python3遍历字节字符串的时候,得到的每个元素是字节所对应的ASCII编码(int整数);遍历字节字符串时,需要bytes处理每个字符然后在做输出。

# Python 2 only:
for bytechar in 'byte-string with high-bit chars like \xf9':
    pass
# Python 3 only:
In>> for b in b'a': 
In>>     print(b)
Out>> 97

# 正确写法
for myint in b'byte-string with high-bit chars like \xf9':
    bytechar = bytes([myint])

有两种兼容写法

  1. 引入future中的bytes
# Python 2 and 3:
from builtins import bytes
for myint in bytes(b'byte-string with high-bit chars like \xf9'):
    bytechar = bytes([myint])
  1. 也可以用chr() and .encode('latin-1')把一个对应的int整数转化成一个字节符
# Python 3 only:
for myint in b'byte-string with high-bit chars like \xf9':
    char = chr(myint)     # 把一个int转成一个unicode字符
    bytechar = char.encode('latin-1')  # 把一个unicode字符转成一个字节符

兼容性写法需要引入builtins包中的bytes和chr

# Python 2 and 3:
from builtins import bytes, chr
for myint in bytes(b'byte-string with high-bit chars like \xf9'):
    char = chr(myint)     # 把一个int转成一个unicode字符
    bytechar = char.encode('latin-1')    # 把一个unicode字符转成一个字节符

basestring

python3中删除了basestring关键字,python2中basestring需要做兼容修改

# python 2:
In>> isinstance(u'abc', basestring) and isinstance(b'abc', basestring)
Out>> True
  1. 引入future包中的basestring,对python3做兼容
# Python 2 and 3:
from past.builtins import basestring
a = u'abc'
b = b'def'
In>> assert (isinstance(a, basestring) and isinstance(b, basestring))
Out>> True
  1. 可以引入builtins中的str,把bytes都转成str然后做判断
# Python 2 and 3:

from builtins import str
a = u'abc'
b = b'def'
c = b.decode()
In>> isinstance(a, str) and isinstance(c, str)
Out>> True
In>> isinstance(a, str) and isinstance(b, str)
Out>> False
In>> isinstance(a, str) and isinstance(b, bytes)
Out>> True

encode 和 decode

python3中str类型的数据不提供decode()方法(因为str类型数据表示的是unicode,本身就不需要decode),python2中的unicode数据避免使用decode。

# python 2:
In>> u'abc'.decode()
Out>> u'abc'
# python 3:
In>> u'abc'.decode()
Out>> <ipython-input-163-f76c289c8e5d> in <module>
----> 1 u'abc'.decode()

AttributeError: 'str' object has no attribute 'decode'

StringIO

StringIO包变化,python3中加入BytesIO,使两者用法分开。

# Python 2 only:
from StringIO import StringIO
# or:
from cStringIO import StringIO

# Python 2 and 3:
from io import BytesIO     # 处理 byte 字符串
from io import StringIO    # 处理 unicode 字符串

字典

iterkeys/itervalues/iteritems

python3中删除了 dict.iterkeys()dict.itervalues()dict.iteritems() 的写法;修改 dict.keys(), dict.values(), dict.items() 作为一个可迭代对象,而不是list。

heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
# Python 2 only:
for key in heights.iterkeys():
    pass

# Python 2 and 3:
for key in heights:
    pass

如果不需要获取迭代器,直接使用 keys() 等方法就行,如果还是需要迭代器需要做兼容

# Python 2 and 3:
from future.utils import itervalues, itervalues, iteritems
# or
from six import iterkeys, itervalues, iteritems

for key in iterkeys(heights):
    pass
    
for value in itervalues(heights):
    pass

for (key, value) in iteritems(heights):
    pass

keys/values/items

python3中 dict.keys(), dict.values(), dict.items() 的返回值是一个可迭代对象,不能通过下标获取。如果需要兼容python2的下标用法,需要把转换成把keys/values/items转换成list。

字典 keys 转换成list

 Python 2 only:
keylist = heights.keys()
In>> isinstance(keylist, list)
Out>> True

# Python 2 and 3:
keylist = list(heights)
In>> isinstance(keylist, list)
Out>> True

字典 values 转换成list

# Python 2 only:
heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
valuelist = heights.values()
In>> isinstance(valuelist, list)
Out>> True
# Python 2 and 3: 
# 方法 1
valuelist = list(heights.values())  # 效率比较低,数据量少没啥影响

# 方法2,利用future包中的dict
from builtins import dict
heights = dict(Fred=175, Anne=166, Joe=192)
valuelist = list(heights.values())

# 方法3,利用future中的listvalues
from future.utils import itervalues
# or
from six import itervalues
valuelist = list(itervalues(heights))

字典 items 转换成list

# Python 2 and 3: 
# 方法1
itemlist = list(heights.items())    # 效率比较低,数据量少没啥影响

# 方法2, 利用future中的iteritems方法
from future.utils import iteritems
# or
from six import iteritems

itemlist = list(iteritems(heights))

一些返回list或迭代器的相似方法

range and xrange

Python3 删除了 xrangerange() 函数返回一个可迭代对象(类型是对象),而不是列表类型。

  • xrange兼容
# Python 2 only:
for i in xrange(10**8):
    pass

# 向前兼容
# Python 2 and 3:
from builtins import range
for i in range(10**8):
    pass

# 向后兼容
# Python 2 and 3:
from past.builtins import xrange
for i in xrange(10**8):
    pass
  • range兼容
# Python 2 only
In>> range(5)
Out>> [0, 1, 2, 3, 4]

# 方法1: 向前兼容
# Python 2 and 3:
In>> list(range(5))
Out>> [0, 1, 2, 3, 4]

# 方法2: 向前兼容,利用future中的range,推荐,可兼容xrange
# Python 2 and 3:
from builtins import range
In>> list(range(5))
Out>> [0, 1, 2, 3, 4]

# 方法3:
# Python 2 and 3:
from future.utils import lrange
In>> lrange(5)
Out>>  [0, 1, 2, 3, 4]

# 方法4: 向后兼容
# Python 2 and 3:
from past.builtins import range
In>> range(5)
Out>> [0, 1, 2, 3, 4]

map 和 imap

python3中删除了 imap()map() 返回一个可迭代对象,而不是list;python2中map需要做兼容。

  • map
# Python 2 only:
In>> map(int, [1, 2, 3])
Out>> [1, 2, 3]

# 方法1:
# Python 2 and 3: option 1
In>> list(map(int, [1, 2, 3]))
Out>> [1, 2, 3]

# 方法2: map返回迭代器,更符合python3,推荐
# Python 2 and 3:
from builtins import map
In>> list(map(int, [1, 2, 3]))
Out>> [1, 2, 3]

# 方法3:
# Python 2 and 3:
from future.utils import lmap
In>> lmap(int, [1, 2, 3])
Out>> [1, 2, 3]

# 方法4:
# Python 2 and 3:
from past.builtins import map
In>> map(int, [1, 2, 3])
Out>> [1, 2, 3]
  • imap
# Python 2 only:
from itertools import imap
In>> myiter = imap(int, [1, 2, 3])
Out>> <itertools.imap at 0x1017bd1d0>

# Python 2 and 3:
from builtins import map
In>> map(int, [1, 2, 3])
Out>> <itertools.imap at 0x101ab6c10>

zip 和 izip

和上面的兼容方式以上,zipitertools.izip

filter 和 ifilter

和上面的兼容方式以上,filteritertools.ifilter

其他一些内建方法

reduce()

python3中 reduce 方法需要从 functools 中引入。

# Python 2 only:
In>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
Out>> 15

# Python 2 and 3:
from functools import reduce
In>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
Out>> 15

open()

python2中的 open 是以二进制打开,python3中 是以 unicode 方式打开。python2中需要用io包中的 open 做兼容。

# Python 2 only
f = open('myfile.txt')
data = f.read()              # 返回字节字符串
text = data.decode('utf-8')  # 转成unicode

# 方法1:
# Python 2 and 3: 
from io import open
f = open('myfile.txt', 'rb') # 二进制形式打开
data = f.read()              # 返回字节
text = data.decode('utf-8')  # 转成字符串

# 方法2:
# Python 2 and 3:
from io import open
f = open('myfile.txt', encoding='utf-8')  # 直接以unicode方式打开
text = f.read()                           # read直接返回unicode

file()

python3删除了 file() 方法,python2中应该用 open()

# Python 2 only:
f = file(pathname)

# Python 2 and 3:
f = open(pathname)

# 最好使用这种方式,可以更好的兼容open:
from io import open
f = open(pathname, 'rb')   # if f.read() should return bytes
# or
f = open(pathname, 'rt')   # if f.read() should return unicode text

raw_input() 和 input()

python3中去掉了 raw_input(),可使用 input()代替。

# Python 2 only: 
In>> raw_input('What is your name? ')
What is your name? hello
Out>> 'hello'

In>> input("Type something safe please: ")
Type something safe please: 'hello'
Out>> 'hello'


# Python 2 and 3:
from builtins import input

In>> input('Type something safe please: ')
Type something safe please: hello
Out>> 'hello'
In>> input("Type something safe please: ")
Type something safe please: 'hello'
Out>> "'hello'"

exec() 和 execfile()

python3不支持 exec+空格的写法,需要改成 exec()

# Python 2 only:
exec 'x = 10'

# Python 2 and 3:
exec('x = 10')
# 定义全局变量:
# Python 2 only:
g = globals()
exec 'x = 10' in g

# Python 2 and 3:
g = globals()
exec('x = 10', g)
# Python 2 only:
l = locals()
exec 'x = 10' in g, l

# Python 2 and 3:
exec('x = 10', g, l)

python3中删除了 execfile(),python2中需要做兼容。

# Python 2 only:
execfile('myfile.py')

# 方法1: 因入future中的execfile
# Python 2 and 3:
from past.builtins import execfile
execfile('myfile.py')

# 方法2: 读取文件内容,然后用exec执行
# Python 2 and 3:
exec(open('myfile.py').read())

unichr() 和 chr()

python2中 chr() 返回字节符,参数限定大小最大为255; unichr() 返回unicode字符。

python3中删除了 unichr()chr()返回对应的str字符,没有长度限制。

# Python 2 only:
In>> chr(97)
Out>> 'a'
In>> isinstance(chr(97), bytes)
Out>> True

In>> unichr(97)
out>> u'a'      # unicode
In>> unichr(20013)
Out>> u'\u4e2d'   # unicode
# Python 3 only:
In>> chr(97)
Out>> 'a'       # unicode,python中str类型
In>> chr(20013)
Out>> '中'      # unicode,python中str类型
In>> chr(20013).encode()
Out>> b'\xe4\xb8\xad'

兼容性写法:

# Python 2 and 3:
from builtins import chr
In>> chr(20013)
Out>> u'\u4e2d'

intern()

# Python 2 only:
intern('mystring')
# Python 3 only:
from sys import intern
intern('mystring')

兼容性写法:

# 方法1: 
# Python 2 and 3:
from past.builtins import intern
intern('mystring')

# 方法2:
# Python 2 and 3:
from six.moves import intern
intern('mystring')

# 方法3:
# Python 2 and 3: 
from future.standard_library import install_aliases
install_aliases()
from sys import intern
intern('mystring')

apply()

python3中删除了 apply() 方法

def func_f(a, b, kwarg1=None):
    pass

# 一些参数
args = ('a', 'b')
kwargs = {'kwarg1': True}
# Python 2 only:
apply(func_f, args, kwargs)  # 把args和kwargs带入,执行func_f

# 方法1: 直接调用func_f,传参,推荐
# Python 2 and 3: 
func_f(*args, **kwargs)

# 方法2: 引入future中的apply
# Python 2 and 3:
from past.builtins import apply
apply(f, args, kwargs)

cmp()

python3中删除了 cmp() 方法;可以使用 (x>y)-(x<y) 实现,或者用 operator 中的 lelteq 等替换。

# Python 2 only:
In>> cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
Out>> True
# 方法1: 自定义一个cmp方法
# Python 2 and 3: alternative 2
cmp = lambda(x, y): (x > y) - (x < y)
In>> cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
Out>> True

# 方法1: 引入future中的cmp
# Python 2 and 3: 
from past.builtins import cmp
In>> cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
Out>> True

注意:python3中 sorted 方法已经取消了cmp参数。

# python 2:
sorted(iterable, cmp=None, key=None, reverse=False)

# python 3:
sorted(iterable, key=None, reverse=False) 

reload()

# Python 2 only:
reload(mymodule)

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

自定义类时的一些习惯变化

自定义迭代器

# Python 2 only
class Upper(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
    def next(self):          # Py2风格
        return self._iter.next().upper()
    def __iter__(self):
        return self

In>> itr = Upper('hello')
In>> itr.next()     # Py2风格
Out>> 'H'
In>> list(itr) == list('ELLO')
Out>> True
# 方法1:
# Python 2 and 3:
from builtins import object

class Upper(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
    def __next__(self):                  # Py3风格
        return next(self._iter).upper()  # 在这调用内建方法 next()
    def __iter__(self):
        return self

In>> itr = Upper('hello')
In>> next(itr)      # 兼容方式
Out>> 'H'
In>> list(itr) == list('ELLO')
Out>> True
# 方法2:
# Python 2 and 3:
from future.utils import implements_iterator

@implements_iterator
class Upper(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
    def __next__(self):                  # Py3风格
        return next(self._iter).upper()  # 在这调用内建方法 next()
    def __iter__(self):
        return self

In>> itr = Upper('hello')
In>> next(itr)      # 兼容方式
Out>> 'H'
In>> list(itr) == list('ELLO')
Out>> True

自定义 str 方法

# Python 2 only:
class MyClass(object):
    def __unicode__(self):
        return u'Unicode string: \u5b54\u5b50'
    def __str__(self):
        return unicode(self).encode('utf-8')

In>> a = MyClass()
In>> print(a)    # 打印unicode
Out>> Unicode string: 孔子
# Python 2 and 3:
from future.utils import python_2_unicode_compatible

@python_2_unicode_compatible
class MyClass(object):
    def __str__(self):
        return u'Unicode string: \u5b54\u5b50'

In>> a = MyClass()
In>> print(a)    # 打印unicode
Out>> Unicode string: 孔子

自定义 nonzero vs bool

# Python 2 only:
class AllOrNothing(object):
    def __init__(self, l):
        self.l = l
    def __nonzero__(self):
        return all(self.l)

In>> container = AllOrNothing([0, 100, 200])
In>> not bool(container)
Out>> True
# Python 2 and 3:
from builtins import object

class AllOrNothing(object):
    def __init__(self, l):
        self.l = l
    def __bool__(self):
        return all(self.l)

In>> container = AllOrNothing([0, 100, 200])
In>> not bool(container)
Out>> True

相对位置包的引入

例如下面这个目录结构

mypackage/
    __init__.py
    submodule1.py
    submodule2.py

下面代码在 submodule1.py

# Python 2 only:
import submodule2                #隐式相对导入,在python3中实际上已经被禁用
from . import submodule2         #显式相对导入
from package1 import submodule2  #显式绝对导入

# Python 2 and 3:
from . import submodule2

python2缺省为相对路径导入,python3缺省为绝对路径导入,实际上隐式相对导入不太友好,如果有重名的包可能会造成混淆。

需要禁止python2的隐式相对导入,来保证引入的正确性,在文件头加上 from future import absolute_import 改为绝对路径导入

# Python 2 and 3:
from __future__ import absolute_import

一些常用包和模块的变更

StringIO 模块

# Python 2 only
from StringIO import StringIO
from cStringIO import StringIO

# Python 2 and 3
from io import BytesIO
from io import StringIO

http 模块

# Python 2 only:
import httplib
import Cookie
import cookielib
import BaseHTTPServer
import SimpleHTTPServer
import CGIHttpServer

# Python 2 and 3: 需要安装future包
import http.client
import http.cookies
import http.cookiejar
import http.server

html escaping and entities

# Python 2 and 3:
from cgi import escape

# Python 2 and 3: 需要安装future包
from html import escape
# Python 2 only:
from htmlentitydefs import codepoint2name, entitydefs, name2codepoint

# Python 2 and 3: 需要安装future包
from html.entities import codepoint2name, entitydefs, name2codepoint

html parsing

# Python 2 only:
from HTMLParser import HTMLParser

# 方法1:
# Python 2 and 3: 需要安装future包
from html.parser import HTMLParser

# 方法2:
# Python 2 and 3:
from future.moves.html.parser import HTMLParser

xmlrpc 模块

# Python 2 only:
import DocXMLRPCServer
import SimpleXMLRPCServer

# Python 2 and 3: 需要安装future包
import xmlrpc.server
# Python 2 only:
import xmlrpclib

# Python 2 and 3: 需要安装future包
import xmlrpc.client

urllib 模块

urllib 模版改动较大,python3中移除了 urllib2 包,官方也指出 urllib 包是python 2/3中兼容性最难写的代码了,官方建议使用 requests 包代替。

urllib is the hardest module to use from Python 2/3 compatible code. You may like to use Requests (http://python-requests.org) instead.

这里也给出兼容性写法

# Python 2 only:
from urlparse import urlparse
from urllib import urlencode
from urllib2 import urlopen, Request, HTTPError
# Python 3 only:
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
# 方法1:最简单的兼容写法
# Python 2 and 3:
from future.standard_library import install_aliases
install_aliases()

from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError

# 方法2:
# Python 2 and 3:
from future.standard_library import hooks

with hooks():
    from urllib.parse import urlparse, urlencode
    from urllib.request import urlopen, Request
    from urllib.error import HTTPError
    
# 方法3:
# Python 2 and 3:
from future.moves.urllib.parse import urlparse, urlencode
from future.moves.urllib.request import urlopen, Request
from future.moves.urllib.error import HTTPError
# or
from six.moves.urllib.parse import urlparse, urlencode
from six.moves.urllib.request import urlopen
from six.moves.urllib.error import HTTPError

# 方法4:
# Python 2 and 3: 
try:
    from urllib.parse import urlparse, urlencode
    from urllib.request import urlopen, Request
    from urllib.error import HTTPError
except ImportError:
    from urlparse import urlparse
    from urllib import urlencode
    from urllib2 import urlopen, Request, HTTPError

socketserve

# Python 2 only:
import SocketServer

# Python 2 and 3: 需要安装future包
import socketserver

copy_reg, copyreg

# Python 2 only:
import copy_reg

# Python 2 and 3: 需要安装future包
import copyreg

configparser

# Python 2 only:
from ConfigParser import ConfigParser

# Python 2 and 3: 需要安装future包
from configparser import ConfigParser

queue 模块

# Python 2 only:
from Queue import Queue, heapq, deque

# Python 2 and 3: 需要安装future包
from queue import Queue, heapq, deque

repr, reprlib

# Python 2 only:
from repr import aRepr, repr

# Python 2 and 3: 需要安装future包
from reprlib import aRepr, repr

itertools 模块中的 filterfalse, zip_longest

# Python 3 only:
from itertools import filterfalse, zip_longest
# Python 2 only:
from itertools import ifilterfalse, izip_longest

# Python 2 and 3: alternative 1
from future.moves.itertools import filterfalse, zip_longest

# Python 2 and 3: alternative 2
from six.moves import filterfalse, zip_longest

# Python 2 and 3: alternative 3
from future.standard_library import install_aliases
install_aliases()
from itertools import filterfalse, zip_longest

UserDict, UserList, UserString

# Python 3 only:
from collections import UserDict, UserList, UserString
# Python 2 only:
from UserDict import UserDict
from UserList import UserList
from UserString import UserString

# Python 2 and 3: alternative 1
from future.moves.collections import UserDict, UserList, UserString

# Python 2 and 3: alternative 2
from six.moves import UserDict, UserList, UserString

# Python 2 and 3: alternative 3
from future.standard_library import install_aliases
install_aliases()
from collections import UserDict, UserList, UserString

Tkinter 模块

# Python 2 only:
import Tkinter
import Dialog
import FileDialog
import ScrolledText
import SimpleDialog
import Tix
import Tkconstants
import Tkdnd
import tkColorChooser
import tkCommonDialog
import tkFileDialog
import tkFont
import tkMessageBox
import tkSimpleDialog
import ttk

# Python 2 and 3: 需要安装future包
import tkinter
import tkinter.dialog
import tkinter.filedialog
import tkinter.scrolledtext
import tkinter.simpledialog
import tkinter.tix
import tkinter.constants
import tkinter.dnd
import tkinter.colorchooser
import tkinter.commondialog
import tkinter.filedialog
import tkinter.font
import tkinter.messagebox
import tkinter.simpledialog
import tkinter.ttk
posted @ 2020-05-12 11:17  凌晨黄昏  阅读(1027)  评论(0编辑  收藏  举报