Python 2.7.x 与 Python 3.x 的不同点1 __future__模块2 print函数3 整除4 Unicode5 xrange模块6 Python3中的range对象的__con

本文翻译自:《Key differences between Python 2.7.x and Python 3.x》

许多 Python 初学者想知道他们应该从 Python 的哪个版本开始学习。对于这个问题我的答案是 “你学习你喜欢的教程的版本,然后检查他们之间的不同。” 但如果你并未了解过两个版本之间的差异,个人推荐使用 Python 2.7.x 版本,毕竟大部分教材等资料还是用Python 2.7.x来写的。

但是如果你开始一个新项目,并且有选择权?我想说的是目前没有对错,只要你计划使用的库 Python 2.7.x 和 Python 3.x 双方都支持的话。尽管如此,当在编写它们中的任何一个的代码,或者是你计划移植你的项目的时候,是非常值得看看这两个主要流行的 Python 版本之间的差别的,以便避免常见的陷阱。


1 __future__模块

Python 3.x 介绍的 一些Python 2 不兼容的关键字和特性可以通过在 Python 2 的内置__future__ 模块导入。如果你计划让你的代码支持 Python 3.x,建议你使用 __future__模块导入。例如,如果我想要 在Python 2 中表现 Python 3.x 中的整除,我们可以通过如下导入:

from __future__ import division

更多的 future 模块可被导入的特性被列在下表中:

(来源: https://docs.python.org/2/library/future.html


2 print函数

很琐碎,而print语法的变化可能是最广为人知的了,但是仍值得一提的是: Python 2 的 print 声明已经被 print()函数取代了,这意味着我们必须包装我们想打印在小括号中的对象。 Python 2 不具有额外的小括号问题。但对比一下,如果我们按照 Python 2 的方式不使用小括号调用print 函数,Python 3 将抛出一个语法异常(SyntaxError)。

Python 2

print 'Python', python_version()
print 'Hello, World!'
print('Hello, World!')
print "text", ; print 'print more text on the same line'

运行结果如下:

Python 2.7.6
Hello, World!
Hello, World!
text print more text on the same line

Python 3

print('Python', python_version())
print('Hello, World!')
print("some text,", end="") 
print(' print more text on the same line')

运行结果如下:

Python 3.4.1
Hello, World!
some text, print more text on the same line

Python 3

print 'Hello, World!'

运行结果如下:

File ““, line 1
  print ‘Hello, World!’
          ^
SyntaxError: invalid syntax

以上通过 Python 2 使用 Printing "Hello, World" 是非常正常的,尽管如此,如果你有多个对象在小括号中,我们将创建一个元组,因为print 在 Python 2 中是一个声明,而不是一个函数调用。 print 'Python', python_version() print('a', 'b') print 'a', 'b' 运行结果如下: Python 2.7.7 (‘a’, ‘b’) a b


3 整除

如果你正在移植代码,这个变化是特别危险的。或者你在 Python 2 上执行 Python 3 的代码。因为这个整除的变化表现在它会被忽视(即它不会抛出语法异常)。

因此,我还是倾向于使用一个float(3)/23/2.0代替在我的 Python 3 脚本保存在 Python 2 中的 3/2的一些麻烦(并且反而过来也一样,我建议在你的 Python 2 脚本中使用 from __future__ import division)。

Python 2

print 'Python', python_version()
print '3 / 2 =', 3 / 2
print '3 // 2 =', 3 // 2
print '3 / 2.0 =', 3 / 2.0
print '3 // 2.0 =', 3 // 2.0

运行结果如下:

Python 2.7.6
3 / 2 = 1
3 // 2 = 1
3 / 2.0 = 1.5
3 // 2.0 = 1.0

Python 3

print('Python', python_version())
print('3 / 2 =', 3 / 2)
print('3 // 2 =', 3 // 2)
print('3 / 2.0 =', 3 / 2.0)
print('3 // 2.0 =', 3 // 2.0)

运行结果如下:

Python 3.4.1
3 / 2 = 1.5
3 // 2 = 1
3 / 2.0 = 1.5
3 // 2.0 = 1.0

4 Unicode

Python 2 有 ASCII str() 类型,unicode()是单独的,不是 byte 类型。 现在, 在 Python 3,我们最终有了 Unicode (utf-8)字符串,以及一个字节类:bytebytearrays

Python 2

print 'Python', python_version()

运行结果如下:

Python 2.7.6
print type(unicode('this is like a python3 str type'))

运行结果如下:

< type ‘unicode’ >
print type(b'byte type does not exist')

运行结果如下:

< type ‘str’ >
print 'they are really' + b' the same'

运行结果如下:

they are really the same
print type(bytearray(b'bytearray oddly does exist though'))

运行结果如下:

< type ‘bytearray’ >

Python 3

print('Python', python_version())
print('strings are now utf-8 \u03BCnico\u0394é!')

运行结果如下:

Python 3.4.1
strings are now utf-8 μnicoΔé!
print('Python', python_version(), end="")
print(' has', type(b' bytes for storing data'))

运行结果如下:

Python 3.4.1 has < class ‘bytes’ >
print('and Python', python_version(), end="")
print(' also has', type(bytearray(b'bytearrays')))

运行结果如下:

and Python 3.4.1 also has < class ‘bytearray’>

5 xrange模块

在 Python 2 中xrange()创建迭代对象的用法是非常流行的。比如: for循环或者是列表/集合/字典推导式。 这个表现十分像生成器(比如。“惰性求值”)。但是这个 xrange-iterable是无穷的,意味着你可以无限遍历。 由于它的惰性求值,如果你不得仅仅不遍历它一次,xrange() 函数 比range()更快(比如 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代多次,因为生成器每次都从头开始。 在 Python 3 中,range()是像 xrange() 那样实现以至于一个专门的 xrange()函数都不再存在(在 Python 3 中xrange()会抛出命名异常)。

import timeit
n = 10000
def test_range(n):
    return for i in range(n):
        pass
def test_xrange(n):
    for i in xrange(n):
        pass

Python 2

print 'Python', python_version()
print '\ntiming range()'
%timeit test_range(n)
print '\n\ntiming xrange()'
%timeit test_xrange(n)

运行结果如下:

Python 2.7.6
timing range()
1000 loops, best of 3: 433 µs per loop
timing xrange()
1000 loops, best of 3: 350 µs per loop

Python 3

print('Python', python_version())
print('\ntiming range()')
%timeit test_range(n)

运行结果如下:

Python 3.4.1
timing range()
1000 loops, best of 3: 520 µs per loop
print(xrange(10))

运行结果如下:

NameError Traceback (most recent call last)
in ()
——> 1 print(xrange(10))
NameError: name ‘xrange’ is not defined

6 Python3中的range对象的__contains__方法

另外一件值得一提的事情就是在 Python 3 中range有一个新的__contains__方法(感谢 Yuchen Ying 指出了这个),__contains__方法可以加速 “查找” 在 Python 3.x 中显著的整数和布尔类型。

x = 10000000
def val_in_range(x, val):
    return val in range(x)
def val_in_xrange(x, val):
    return val in xrange(x)
print('Python', python_version())
assert(val_in_range(x, x/2) == True)
assert(val_in_range(x, x//2) == True)
%timeit val_in_range(x, x/2)
%timeit val_in_range(x, x//2)

运行结果如下:

Python 3.4.1
1 loops, best of 3: 742 ms per loop
1000000 loops, best of 3: 1.19 µs per loop

基于以上的 timeit 的结果,当它使一个整数类型,而不是浮点类型的时候,你可以看到执行查找的速度是 60000 倍快。尽管如此,因为 Python 2.x 的range 或者是xrange 没有一个__contains__ 方法,这个整数类型或者是浮点类型的查询速度不会相差太大。

print 'Python', python_version()
assert(val_in_xrange(x, x/2.0) == True)
assert(val_in_xrange(x, x/2) == True)
assert(val_in_range(x, x/2) == True)
assert(val_in_range(x, x//2) == True)
%timeit val_in_xrange(x, x/2.0)
%timeit val_in_xrange(x, x/2)
%timeit val_in_range(x, x/2.0)
%timeit val_in_range(x, x/2)

运行结果如下:

Python 2.7.7
1 loops, best of 3: 285 ms per loop
1 loops, best of 3: 179 ms per loop
1 loops, best of 3: 658 ms per loop
1 loops, best of 3: 556 ms per loop

下面说下__contain__方法并没有加入到 Python 2.x 中的证据:

print('Python', python_version())
range.__contains__

运行结果如下:

Python 3.4.1
< slot wrapper ‘contains‘ of ‘range’ objects >
print 'Python', python_version()
range.__contains__

运行结果如下:

Python 2.7.7
AttributeError Traceback (most recent call last)
< ipython-input-7-05327350dafb> in < module>()
1 print ‘Python’, pythonversion()
——> 2 range.`_contains`
AttributeError: ‘builtinfunctionor_method’ object has no attribute `’__contains‘`
print 'Python', python_version()
xrange.__contains__

运行结果如下:

Python 2.7.7
AttributeError Traceback (most recent call last)
< ipython-input-8-7d1a71bfee8e> in < module>()
1 print ‘Python’, pythonversion()
——> 2 xrange.`_contains`
AttributeError: type object ‘xrange’ has no attribute '__contains__'

注意在 Python 2 和 Python 3 中速度的不同 有些人指出了 Python 3 的 range() 和 Python 2 的 xrange() 之间的速度不同。因为他们是用相同的方法实现的,因此期望相同的速度。尽管如此,这事实在于 Python 3 倾向于比 Python 2 运行的慢一点。

def test_while():
    i = 0
    while i < 20000:
        i += 1
    return

Python 3

print('Python', python_version())
%timeit test_while()

运行结果如下:

Python 3.4.1
100 loops, best of 3: 2.68 ms per loop

Python 2

print 'Python', python_version()
%timeit test_while()

运行结果如下:

Python 2.7.6
1000 loops, best of 3: 1.72 ms per loop

7 抛出异常

Python 2 接受新旧两种语法标记,在 Python 3 中如果我不用小括号把异常参数括起来就会阻塞(并且反过来引发一个语法异常)。 Python 2

print 'Python', python_version()

运行结果如下:

Python 2.7.6
raise IOError, "file error"

运行结果如下:

IOError Traceback (most recent call last)
< ipython-input-8-25f049caebb0> in < module>()
——> 1 raise IOError, “file error”
IOError: file error
raise IOError("file error")

运行结果如下:

IOError Traceback (most recent call last)
< ipython-input-9-6f1c43f525b2> in < module>()
——> 1 raise IOError(“file error”)
IOError: file error

Python 3

print 'Python', python_version()

运行结果如下:

Python 3.4.1
raise IOError, "file error"

运行结果如下:

File ““, line 1
raise IOError, “file error”
      ^
SyntaxError: invalid syntax

在 Python 3 中,可以这样抛出异常:

print('Python', python_version())
raise IOError("file error")

运行结果如下:

Python 3.4.1
OSError Traceback (most recent call last)
< ipython-input-11-c350544d15da> in < module>()
1 print(‘Python’, python_version())
——> 2 raise IOError(“file error”)
OSError: file error

8 处理异常

在 Python 3 中处理异常也轻微的改变了,在 Python 3 中我们现在使用 as 作为关键词。 Python 2

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

运行结果如下:

Python 2.7.6
name ‘let_us_cause_a_NameError’ is not defined —> our error message

Python 3

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

运行结果如下:

Python 3.4.1
name ‘let_us_cause_a_NameError’ is not defined —> our error message

9 next()函数和.next()方法

因为 next() (.next())是一个如此普通的使用函数(方法),这里有另外一个语法改变(或者是实现上改变了),值得一提的是:在 Python 2.7.5 中函数和方法你都可以使用,next()函数在 Python 3 中一直保留着(调用.next()抛出属性异常)。 Python 2

print 'Python', python_version()
my_generator = (letter for letter in 'abcdefg')
next(my_generator)
my_generator.next()

运行结果如下:

Python 2.7.6
‘b

Python 3

print('Python', python_version())
my_generator = (letter for letter in 'abcdefg')
next(my_generator)

运行结果如下:

Python 3.4.1
‘a’
1
my_generator.next()

运行结果如下:

AttributeError Traceback (most recent call last)
< ipython-input-14-125f388bb61b> in < module>()
——> 1 my_generator.next()
AttributeError: ‘generator’ object has no attribute ‘next’

10 For循环变量和全局命名空间泄漏

好消息:在 Python 3.x 中for循环变量不会再导致命名空间泄漏。 在 Python 3.x 中做了一个改变,在 What’s New In Python 3.0 中有如下描述: “列表推导不再支持[... for var in item1, item2, ...] 这样的语法。使用[... for var in (item1, item2, ...)]代替。也需要提醒的是列表推导有不同的语义: 他们关闭了在list()构造器中的生成器表达式的语法糖, 并且特别是循环控制变量不再泄漏进周围的作用范围域。

Python 2

print 'Python', python_version()
i = 1
print 'before: i =', i
print 'comprehension: ', [i for i in range(5)]
print 'after: i =', i

运行结果如下:

Python 2.7.6
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 4

Python 3

print('Python', python_version())
i = 1
print('before: i =', i)
print('comprehension:', [i for i in range(5)])
print('after: i =', i)

运行结果如下:

Python 3.4.1
before: i = 1
comprehension: [0, 1, 2, 3, 4]
after: i = 1

11 比较不可排序类型

在 Python 3 中的另外一个变化就是当对不可排序类型做比较的时候,会抛出一个类型错误。 Python 2

print 'Python', 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)

运行结果如下:

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

Python 3

print('Python', 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))

运行结果如下:

Python 3.4.1
TypeError Traceback (most recent call last)
< ipython-input-16-a9031729f4a0> in < module>()
1 print(‘Python’, python_version())
——> 2 print(“[1, 2] > ‘foo’ = “, [1, 2] > ‘foo’)
3 print(“(1, 2) > ‘foo’ = “, (1, 2) > ‘foo’)
4 print(“[1, 2] > (1, 2) = “, [1, 2] > (1, 2))
TypeError: unorderable types: list() > str()

12 通过input()解析用户的输入

幸运的是,在 Python 3 中已经解决了把用户的输入存储为一个 str对象的问题。为了避免在 Python 2 中的读取非字符串类型的危险行为,我们不得不使用raw_input()代替。 Python 2

>>> my_input = input('enter a number: ')
enter a number: 123
>>> type(my_input)
<type 'int'>
>>> my_input = raw_input('enter a number: ')
enter a number: 123
>>> type(my_input)
<type 'str'>

Python 3

>>> my_input = input('enter a number: ')
enter a number: 123
>>> type(my_input)
<class 'str'>

13 返回可迭代对象,而不是列表

如果在 xrange 章节看到的,现在在 Python 3 中一些方法和函数返回迭代对象 — 代替 Python 2 中的列表 因为我们通常那些遍历只有一次,我认为这个改变对节约内存很有意义。尽管如此,它也是可能的,相对于生成器 —- 如需要遍历多次。它是不那么高效的。 而对于那些情况下,我们真正需要的是列表对象,我们可以通过list()函数简单的把迭代对象转换成一个列表。 Python 2

print 'Python', python_version() 
print range(3) 
print type(range(3))

运行结果如下:

Python 2.7.6
[0, 1, 2]
< type ‘list’>

Python 3

print('Python', python_version())
print(range(3))
print(type(range(3)))
print(list(range(3)))

运行结果如下:

Python 3.4.1
range(0, 3)
< class ‘range’>
[0, 1, 2]

在 Python 3 中一些经常使用到的不再返回列表的函数和方法:

  • zip()
  • map()
  • filter()
  • 字典的.keys()方法
  • 字典的.values()方法
  • 字典的.items()方法

更多的关于 Python 2 和 Python 3 的文章

移植到 Python 3 Should I use Python 2 or Python 3 for my development activity? What’s New In Python 3.0 Porting to Python 3 Porting Python 2 Code to Python 3 How keep Python 3 moving forward

Python 3 的拥护者和反对者 10 awesome features of Python that you can’t use because you refuse to upgrade to Python 3 Everything you did not want to know about Unicode in Python 3 Python 3 is killing Python Python 3 can revive Python Python 3 is fine

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏狮乐园

dart class overview

最近在折腾 flutter 相关的东西,所以当然要撸一下 dart 了。编程语言这个东西,接触得多了学习起来速度会提升不少,但是不同的语言具有不同的特色,我们需...

12220
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-07(02).总结private,this,封装,static,成员方法变量,局部变量匿名对象

9:面向对象的练习题(掌握) (1)标准的手机类的定义和测试 (2)Demo类有求和方法,Test类进行测试。 什么时候定义成员变量? 当该变量是用来描...

37830
来自专栏java一日一条

Swift 中的内存管理详解

这篇文章是在阅读《The Swift Programming Language》Automatic Reference Counting(ARC,自动引用计数)...

13710
来自专栏云瓣

读书笔记-你不知道的JavaScript(上)

本文首发在我的个人博客:http://muyunyun.cn/ 《你不知道的JavaScript》系列丛书给出了很多颠覆以往对JavaScript认知的点...

419100
来自专栏烂笔头

Python魔术方法-Magic Method

目录[-] 介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,例如类的初始化方法 __init__ ,Pyt...

36360
来自专栏从零开始学 Web 前端

从零开始学 Web 之 JavaScript(四)数组

arguements 是伪数组:不能修改长短的数组。(可以修改元素,但是不能变长变短)

9520
来自专栏个人随笔

Java 中的异常处理机制

生活中的异常:  不能够完整而顺利的完成一些工作  根据不同的异常进行相应的处理,而不会就此终端我们的生活 引出:   异常处理:   方式:     ...

365100
来自专栏偏前端工程师的驿站

一起Polyfill系列:Function.prototype.bind的四个阶段

昨天边参考es5-shim边自己实现Function.prototype.bind,发现有不少以前忽视了的地方,这里就作为一个小总结吧。 一、Function....

19160
来自专栏技术点滴

不要被C++“自动生成”所蒙骗

不要被C++“自动生成”所蒙骗 C++对象可以使用两种方式进行创建:构造函数和复制构造函数。假如我们定义了类A,并使用它创建对象。 A a,b; A c=a; ...

23990
来自专栏醒者呆

正则表达式——Java程序员懂你

正则表达式 关键字:正则表达式,Pattern,Matcher,字符串方法,split,replace 前文书立下了一个flag,这里要把它完成,就是正则...

39150

扫码关注云+社区

领取腾讯云代金券