Python2 和 Python3 的差异及兼容技巧

前语

最近 Python 之父 Guido van Rossum(龟爷)总算在 Python 官方邮件组履行了 Python 2.7 的终焉之日(EOL)。

说的是 Python 2.7 的 EOL 日期终究确认为 2020 年 1 月 1 日,之后不会有任何更新,包含源码的安全补丁。

所以兼容Python3现已能够说十分必要了,但有些常用的库还没有晋级到Python3,所以咱们看下怎么写出兼容2和3的代码。

Python 2 or 3 ?

Python 3 被钦定为 Python 的未来,于 2008 年底发布,是现在正在开发的版别。旨在处理和批改 Python 2 留传的规划缺点、整理代码库冗余、寻求有且仅有一种最佳实践办法来履行使命等问题。


起先,由于 Python 3 不能向后兼容的现实,导致了用户选用缓慢,对初学者不友好等问题。但在 Python 社区的尽力和决绝情绪下,到龟爷宣布邮件之前,现已有了 21903 个 Packages 能够支撑 Python 3.5,其间包含了绝大多数最受欢迎的封装库,与此一起也有越来越多的封装库(e.g. Django、Numpy)表明其新版别将不再支撑 Python 2。


Python 2.7 于 3.0 之后的 2010 年 7 月 3 日发布,方案作为 2.x 的最终一个版别。Python 2.7 的前史使命在于经过供给 2 和 3 之间的兼容性办法,使 Python 2.x 的用户更简略将代码移植到 Python 3.x 上。那么假如你期望自己的代码能够兼容两个不同的版别,首要你最少要让代码能够正常的运转在 Python 2.7 上。


注:下文运用 P2 表明 Python 2.7;运用 P3 表明 Python 3.x。

不同与兼容

__future__ 模块是咱们首要需求了解的,该模块最主要的效果是支撑在 P2 中导入那些在 P3 才收效的模块和函数。是一个十分优异的兼容性东西库,鄙人文中给出的许多 兼容技巧 实例都依赖于它。


特性 在此版别可选 在此版别内置 效果

nested_scopes 2.1.0b1 2.2 PEP 227:静态嵌套效果域

generators 2.2.0a1 2.3 PEP 255:简略生成器

division 2.2.0a2 3.0 PEP 238:除法操作符改动

absolute_import 2.5.0a1 3.0 PEP 328:Imports 多行导入与肯定相对途径

with_statement 2.5.0a1 2.6 PEP 343:with 句子

print_function 2.6.0a2 3.0 PEP 3105:print 句子晋级为函数

unicode_literals 2.6.0a2 3.0 PEP 3112:Bytes 类型

(__future__ 功用列表)


一致不等于语法

P2 支撑运用 <> 和 != 表明不等于。

P3 仅支撑运用 != 表明不等于。

兼容技巧:

一致运用 != 语法


一致整数类型

P2 中整数类型能够细分为短整型 int 和长整型 long。

P3 废除了短整型,并一致运用 int 表明长整型(不再有 L 跟在 repr 后边)。

兼容技巧:

# Python 2 only
k = 9223372036854775808L
# Python 2 and 3:
k = 9223372036854775808
# Python 2 only
bigint = 1L
# Python 2 and 3
from future.builtins import int
bigint = int(1)

一致整数除法

P2 的除法 / 符号实际上具有两个功用:

当两个操作数均为整型目标时,进行的是地板除(截除小数部分),回来整型目标;

当两个操作数存在至少一个浮点型目标时,进行的是真除(保存小数部分),回来浮点型目标。

P3 的除法 / 符号只是具有真除的功用,而地板除的功用则交由 // 来完结。

兼容技巧:

# 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)

一致缩进语法

P2 能够混合运用 tab 和 space 两种办法来进行缩进(1 个 tab == 8 个 space),但实际上这一特性并非一切 IDE 都能够支撑,会因而呈现相同的代码无法跨 IDE 运转的状况。

P3 一致运用 tab 作为缩进,假如 tab 和 space 一起存在,就会触发反常:

TabError: inconsistent use of tabs and spaces in indentation.

兼容技巧:

一致运用 tab 作为缩进。

一致类界说

P2 一起支撑新式类(object)和旧式类。

P3 则一致运用新式类,而且只要运用新式类才干运用多重承继。

兼容技巧:

一致运用新式类。


一致字符编码类型

P2 默许运用 ASCII 字符编码,但由于 ASCII 只支撑数百个字符,并不能灵敏的满意非英文字符,所以 P2 一起也支撑 Unicode 这种更强壮的字符编码。不过,由于 P2 一起支撑两套字符编码,就不免多出了一些标识和转化的费事。

而 P3 一致运用 Unicode 字符编码,这节省了开发者的时刻,一起也能够轻松地在程序中输入和显现更多品种的字符。

兼容技巧:

在一切的字符串赋值中均运用前缀 u,或引进 unicode_literals 字符模块。

# Python 2 only
s1 = 'PythonTab'
s2 = u'PythonTab中文网'
# Python 2 and 3
s1 = u'PythonTab'
s2 = u'PythonTab中文网'
# Python 2 and 3
from __future__ import unicode_literals    # at top of module
s1 = 'PythonTab'
s2 = 'PythonTab中文网'

一致导入模块的途径查找办法

P2 导入一个模块时首要会查找当时目录(cwd),若非,则查找环境变量途径(sys.path)。这一特性经常给开发者带来困扰,信任咱们都从前碰到过,特别当自界说模块与体系模块重名的时分;

为了处理这个问题,默许的 P3 仅会查找环境变量途径,当你需求查找自界说模块时,你能够在包管理模式下将项目途径加入到环境变量中,然后再运用肯定途径和相对途径(以 . 最初)的办法来导入。

兼容技巧:

一致运用肯定途径进行自界说模块导入。


批改列表推导式的变量效果域走漏

P2 的列表推倒式中的变量会走漏到大局效果域,例如:

import platform
print('Python', platform.python_version())
i = 1
print('before: I = %s' % i)
print('comprehension: %s' % [i for i in range(5)])
print('after: I = %s' % i)
# OUT
Python 2.7.6
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 4

P3 则处理了这个问题,列表推倒式中的变量不再走漏到大局效果域。

import platform
print('Python', platform.python_version())
i = 1
print('before: i =', i)
print('comprehension:', [i for i in range(5)])
print('after: i =', i)
# OUT
Python 3.4.1
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 1

批改不合法比较操作反常

P2 能够对两个数据类型并不相同的目标进行比较。

import platform
print('Python', platform.python_version())
print("[1, 2] > 'foo' = ", [1, 2] > 'foo')
print("(1, 2) > 'foo' = ", (1, 2) > 'foo')
print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2))

# OUT

Python 2.7.6
[1, 2] > 'foo' = False
(1, 2) > 'foo' = True
[1, 2] > (1, 2) = False

不过,这种看似便利的特性,实际上却是一个守时炸弹,由于你无法仅有的确认到底是什么原因导致的回来值为 False(可能是数据比较、也可能是数据类型不一致)。


P3 则对其进行了批改,假如比较操作数类型不一致时,会触发 TypeError 反常。

兼容技巧:

永久不要比较数据类型不一致的目标。


一致抛出反常语法

P2 一起支撑新旧两种反常触发语法:

raise IOError, "file error"   # Old
raise IOError("file error")   # New

P3 则一致运用新反常触发语法,否则会触发 SyntaxError 反常:

raise IOError("file error")

兼容技巧:

### 抛出反常
# 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 1
from six import reraise as raise_
# or # from future.utils import raise_
traceback = sys.exc_info()[2]
raise_(ValueError, "dodgy value", traceback)
# Python 2 and 3: option 2
from future.utils import raise_with_traceback
raise_with_traceback(ValueError("dodgy value"))
### 反常链处理
# Setup:
class DatabaseError(Exception):
    pass
# Python 3 only
class 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_from
class FileDatabase:
    def __init__(self, filename):
        try:
            self.file = open(filename)
        except IOError as exc:
            raise_from(DatabaseError('failed to open'), exc)

一致反常处理语法

P2 完成反常处理也能够支撑两种语法。

try:
    let_us_cause_a_NameError
except NameError, err:
# except NameError as err:
    print err, '--> our error message'

P3 的反常处理则强制要求运用 as 关键字的办法。

try:
    let_us_cause_a_NameError
except NameError as err:
    print(err, '--> our error message')

兼容技巧:

一致运用 as 关键字的反常处理办法。

一致输入函数

P2 支撑 raw_input 和 input 两个输入函数,差异在于前者仅能回来 String 类型目标,后者则支撑回来数字和字符串两种数据类型目标,而且当输入为表达式时,会隐式调用 eval 函数回来其履行成果。明显的,运用 input 是愈加灵敏的写法。

所以 P3 一致的运用了 input 函数进行输入处理。


兼容技巧:

一致运用 input 内置函数。

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

一致输出函数

P2 中的 print 便是关键字又是内置函数。print 'Hello world!' 为一条句子,print('Hello world!') 则为一次函数调用。

P3 一致运用 print 函数进行输出操作,其原型如下,这一改动让 P3 的输出处理变得愈加简练、强壮而高雅,经过实参的传递就能代替 P2 中繁复的代码完成。

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

兼容技巧:

### 单行打印单个 String
# Python 2 only:
print 'Hello'
# Python 2 only:
print 'Hello'
### 单行打印多个 String
# 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_function
print('Hello', file=sys.stderr)
### 换行打印
# Python 2 only:
print 'Hello',
# Python 2 and 3:
from __future__ import print_function
print('Hello', end='')

一致文件操作函数

P2 支撑运用 file 和 open 两个函数来进行文件操作。

P3 则一致运用 open 来进行文件操作。

兼容技巧:

一致运用 open 函数。

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

一致列表迭代器生成函数

P2 支撑运用 range 和 xrange 两个函数来生成可迭代目标,差异在于前者回来的是一个列表类型目标,后者回来的是一个相似生成器(慵懒求值)的迭代目标,支撑无限迭代。所以当你需求生成一个很大的序列时,引荐运用 xrange,由于它不会一上来就讨取序列所需的一切内存空间。假如只对序列进行读操作的话,xrange 办法功率明显会更高,可是假如要修正序列的元素,或许往序列增删元素的话,那只能经过 range 办法生成一个 list 目标了。


P3 则一致运用 range 函数来生成可迭代目标,但其实 P3 的 range 更像是 P2 的 xrange。所以在 P3 中假如你想得到一个能够被修正的列表目标,你需求这么做:

list(range(1,10))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

兼容技巧:

一致运用 range 函数

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

一致迭代器迭代函数

P2 中支撑运用内置函数 next 和迭代器目标的 .next() 实例办法这两种办法来获取迭代器目标的下一个元素。所以,在完成自界说迭代器目标类时,有必要完成 .next() 实例办法:

# Python 2 only
class Upper(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
    def next(self):          # Py2-styface iterator interface
        return self._iter.next().upper()
    def __iter__(self):
        return self
itr = Upper('hello')
assert itr.next() == 'H'     # Py2-style
assert list(itr) == list('ELLO')

但在 P3 中一致了运用 next 内置函数来获取下一个元素,假如企图调用 .next() 办法则会触发 AttributeError 反常。所以,在 P3 中完成自界说迭代器所要完成的是 __next__ 特别办法。


兼容技巧:

# Python 2 and 3: option 1
from future.builtins import object
class 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 self
itr = Upper('hello')
assert next(itr) == 'H'      # compatible style
assert list(itr) == list('ELLO')
# Python 2 and 3: option 2
from future.utils import implements_iterator
@implements_iterator
class 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 self
itr = Upper('hello')
assert next(itr) == 'H'
assert list(itr) == list('ELLO')


上一篇:问题处理zipimport.ZipImportError: can‘t decompress data; zlib not availabl
下一篇:Python中lambda表达式的优缺点及运用场景

PythonTab微信大众号:

Python技能交流合作群 ( 请勿加多个群 ):

群1: 87464755

群2: 333646237

群3: 318130924

群4: 385100854