专栏首页python3Python2 和 Python3 的

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 后面)。 兼容技巧:

1 2 3 4

Python 2 only

k = 9223372036854775808L

Python 2 and 3:

k = 9223372036854775808 1 2 3 4 5

Python 2 only

bigint = 1L

Python 2 and 3

from future.builtins import int bigint = int(1) 统一整数除法

P2 的除法 / 符号实际上具有两个功能: 当两个操作数均为整型对象时,进行的是地板除(截除小数部分),返回整型对象; 当两个操作数存在至少一个浮点型对象时,进行的是真除(保留小数部分),返回浮点型对象。 P3 的除法 / 符号仅仅具有真除的功能,而地板除的功能则交由 // 来完成。 兼容技巧:

1 2 3 4 5

Python 2 only:

assert 2 / 3 == 0

Python 2 and 3:

assert 2 // 3 == 0 “True division” (float division): 1 2 3 4

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 同时存在,就会触发异常: 1 TabError: inconsistent use of tabs and spaces in indentation. 兼容技巧:

统一使用 tab 作为缩进。 统一类定义

P2 同时支持新式类(object)和老式类。 P3 则统一使用新式类,并且只有使用新式类才能应用多重继承。 兼容技巧:

统一使用新式类。

统一字符编码类型

P2 默认使用 ASCII 字符编码,但因为 ASCII 只支持数百个字符,并不能灵活的满足非英文字符,所以 P2 同时也支持 Unicode 这种更强大的字符编码。不过,由于 P2 同时支持两套字符编码,就难免多出了一些标识和转换的麻烦。 而 P3 统一使用 Unicode 字符编码,这节省了开发者的时间,同时也可以轻松地在程序中输入和显示更多种类的字符。 兼容技巧:

在所有的字符串赋值中均使用前缀 u,或引入 unicode_literals 字符模块。 1 2 3 4 5 6 7 8 9 10

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 的列表推倒式中的变量会泄露到全局作用域,例如: 1 2 3 4 5 6 7 8 9 10 11 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 则解决了这个问题,列表推倒式中的变量不再泄露到全局作用域。 1 2 3 4 5 6 7 8 9 10 11 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 能够对两个数据类型并不相同的对象进行比较。 1 2 3 4 5 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

1 2 3 4 Python 2.7.6 [1, 2] > 'foo' = False (1, 2) > 'foo' = True [1, 2] > (1, 2) = False 不过,这种看似方便的特性,实际上却是一个定时炸弹,因为你无法唯一的确定到底是什么原因导致的返回值为 False(可能是数据比较、也可能是数据类型不一致)。

P3 则对其进行了修正,如果比较操作数类型不一致时,会触发 TypeError 异常。 兼容技巧:

永远不要比较数据类型不一致的对象。

统一抛出异常语法

P2 同时支持新旧两种异常触发语法: 1 2 raise IOError, "file error" # Old raise IOError("file error") # New P3 则统一使用新异常触发语法,否则会触发 SyntaxError 异常: 1 raise IOError("file error") 兼容技巧:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

抛出异常

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 实现异常处理也能够支持两种语法。 1 2 3 4 5 try:

let_us_cause_a_NameError

except NameError, err:

except NameError as err:

print err, '--> our error message'

P3 的异常处理则强制要求使用 as 关键字的方式。 1 2 3 4 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 内置函数。 1 2 3 4 5

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 中繁复的代码实现。 1 print(*objects, sep=' ', end='n', file=sys.stdout, flush=False) 兼容技巧:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

单行打印单个 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 函数。 1 2 3 4

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 中如果你想得到一个可以被修改的列表对象,你需要这么做: 1 2 list(range(1,10)) [1, 2, 3, 4, 5, 6, 7, 8, 9] 兼容技巧:

统一使用 range 函数 1 2 3 4 5 6 7 8 9 10 11

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() 实例方法: 1 2 3 4 5 6 7 8 9 10 11

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 特殊方法。

兼容技巧:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 8年Python开发的大牛告诉你自学Py

    1.全球三大语言之一. 根据tiobe 最新排名,Python已超越c#于Java,c一起成为全球三大最流行语言。

    py3study
  • Python常见问题

    Python 是一种解释性、交互式、面向对象的编程语言。 它包含了模块、异常、动态类型、非常高层级的动态数据类型以及类的概念。 Python结合了超强的功能和极...

    py3study
  • 现实世界中的 Python

    非常稳定。 自 1991 年起大约每隔 6 到 18 个月就会推出新的稳定发布版,这种状态看来还将持续下去。 目前主要发布版本的间隔通常为 18 个月左右。

    py3study
  • Python之父重回决策层,未来如何发展?

    春节假期结束了,大家陆续地重回到原来的生活轨道上。假期是一个很好的休息与调节的机会,同时,春节还有辞旧迎新的本意,它是新的轮回的开端。

    Python猫
  • Python之父重回决策层,未来如何发展?

    春节假期结束了,大家陆续地重回到原来的生活轨道上。假期是一个很好的休息与调节的机会,同时,春节还有辞旧迎新的本意,它是新的轮回的开端。

    AI科技大本营
  • 这里有 300 篇 Python 与机器学习类原创笔记

    主要包括计算机科学中基本的算法与数据结构,结合算法思想和Leetcode实战,总结介绍。

    好好学java
  • 用《矛盾论》来解读 Python

    大家好,我是猫哥。我对于编程语言跟其它学科的融合非常感兴趣,这种兴趣在创办公众号时就已非常浓烈,因此,几个月来,就做了不少大胆的尝试。不敢说取得了什么“成果”吧...

    Python猫
  • 学习 Python 来做一些神奇好玩的事情吧

    相信看完 @X_AirDu 的回答我们已经对 Python 有了一个大概的了解。那接下来就让我们更深入的了解 Python 吧~

    SunnyGo
  • 用Python支持 7 亿月活用户的应用?Instagram 是这样实现的

    PyCon 简介 PyCon 是全世界最大的以 Python 编程语言 为主题的技术大会。大会由 Python 社区组织,每年举办一次。在大会上,来自世界各...

    AI研习社
  • python和php学习哪个更有发展

    无论是对于刚接触编程的初学者,还是已经工作的程序员,哪一门编程语言更火,更有价值和前景,似乎是永远有争议的话题。

    砸漏

扫码关注云+社区

领取腾讯云代金券