Python2 vs Python3

Python2 和 Python3

最近在面试, 遇到了几个同样的问题, 就是 “python2 和 python3 具体有哪些区别”? 遇到这个问题的时候, 我每次都懵逼, 因为确实除了语法糖以外的区别, 我一个都说不出来. 所以回来之后查了一下资料, 总结出几个主要的区别, 记录下来.

“future” 模块

python3 引入了一些python2 没有的关键字和功能, 如果需要在python2中使用这些功能, 就需要导入__future__模块. 举个例子, 在python3中/是返回一个浮点数, 但是在python2中, 除法操作返回的是一个整数. 如果要在python2中使用python3的除法功能, 就需要引入 __future__模块来支持.

1
2
3
4
5
6
7
8
9
10
11
python 2.7
>>> 1 / 2
0
>>> 1.0 / 2.0
0.5

>>> from __future__ import division
>>> 1 / 2
0.5
>>> 1.0 / 2.0
0.5

“print” 语法糖

在python2的语法当中, 如果需要在屏幕上打印的时候, 仅仅需要pring "something". 但是来到python3, 这个函数被print()替代了.

python2

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

输出

1
2
3
4
Python 2.7.6
Hello, World!
Hello, World!
text print more text on the same line

python3

1
2
3
4
print('Python', python_version())
print('Hello, World!')
print('Some text,', end="")
print(' print more text on the same line')

输出

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

整数除法

整数除法对于python2到python3转换是一个大坑

python2

1
2
3
4
5
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

输出

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

python3

1
2
3
4
5
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)

输出

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

Unicode

python2 有ASCII str()类型, 跟unicode()类型并不一样, 而且在python2中没有byte类型.
python3 有utf-8 的 str()类型. 同时python3也用bytebytearray

Python2

1
2
3
4
5
6
7
8
>>>print 'Python', python_version()
Python 2.7.6
>>>type(unicode('this is like py3 str type'))
<type 'unicode'>
>>>type(b'byte type does not exist')
<type 'str'>
>>>type(bytearray(b'bytearray oddly exist'))
<type 'bytearray'>

Python3

1
2
3
4
5
6
>>>print('Python', python_version())
Python 3.4.1
>>>print('strings are utf-8 \u03BCnico\u0394é!')
strings are now utf-8 μnicoΔé!
>>>print('Python has', type(b' bytes for storing data'))
Python has <class 'bytes'>

xrange

在python2, xrange()主要被用来构建一个可迭代对象, 实现起来跟generator非常像. 但是xrange并不是有限的, 开发者可以无限地迭代下去. 而在python3, xrange()不复存在了, 取而代之的是 range()这个函数. 如果在python3中调用xrange(), 会被抛一个NameError的异常.

1
2
3
4
5
6
7
8
n = 10000
def range_test(n):
return for i in range(n):
pass

def xrange_test(n):
for i in xrange(n):
pass

对于上面的这个简单的程序, py2和py3各自有着不同的表现.

Python2

1
2
3
4
5
6
7
8
9
10
11
print '\ntiming range()'
%timeit range_test(n)

print '\ntiming xrange()'
%timeit xrange_test(n)

>>> timing range()
1000 loops, best of 3: 433 μs per loop

>>> timing xrange()
1000 loops, best of 3: 350 μs per loop

Python3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
print("\ntiming range()")
%timeit range_test(n)

>>> timing range()
1000 loops, best of 3:520 μs per loop.

print(xrange(10))

>>> --------------------------------------------------------------------------
NameError Traceback (most recent call last)

<ipython-input-5-5d8f9b79ea70> in <module>()
----> 1 print(xrange(10))


NameError: name 'xrange' is not defined

再有就是, 在python3中, range对象有一个新的字段, 叫__contains__, 对于判断一个元素是否存在于range能加速(前提是int或者boolean).

1
2
3
4
5
6
7
8
9
10
11
12
13
x = 10000000

def val_in_range(x, val):
return val in range(x)

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)

>>> 1 loops, best of 3: 742 ms per loop
1000000 loops, best of 3: 1.19μs per loop

从上面的结果来看, 如果元素是整数而不是float的话, 速度要快非常非常多.

抛出异常

python2和python3抛出异常的差异, 仅仅存在于语法层面.

python2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
print 'Python, python_version()'
raise IOError, "file error"

>>> Python 2.7.6
>>> ---------------------------------------------------------------------------
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

python3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
raise IOError, "file error"

>>> File "<ipython-input-10-25f049caebb0>", line 1
raise IOError, "file error"
^
SyntaxError: invalid syntax

raise IOError("file error")

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

异常处理

既然py2和py3在抛出异常上有语法上的差别, 那么处理异常方面也同样会有一点点语法上的区别.

python2

1
2
3
4
5
6
try:
let_us_cause_a_NameError
except NameError, err:
print err, '--> our error message'

>>> name 'let_us_cause_a_NameError' is not defined --> our error message

python3

1
2
3
4
5
6
try:
let_us_cause_a_NameError
except NameError as err:
print(err, '--> our error message')

>>> name 'let_us_cause_a_NameError' is not defined --> our error message

next() 和 .next()

在python3当中, .next()方法已经被取消了, 只剩下next()方法可用. python2 两个方法都继续存在. 如果在python3中调用.nxt(), 系统会抛出异常.

for循环变量和全局空间泄露

python3对比python2的一个重大改变要数python3中, for循环的变量并不会被泄露到全局空间里. 官方文档是这么描述的

“List comprehensions no longer support the syntactic form [… for var in item1, item2, …]. Use [… for var in (item1, item2, …)] instead. Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a list() constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.”

python2

1
2
3
4
5
6
7
8
i = 1
print 'before: i = ', i
print 'comprehension: ', [i for i in range(5)]
print 'after: i = ', i

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

pythnon3

1
2
3
4
5
6
7
8
i = 1
print('before: i =', i)
print('comprehension:', [i for i in range(5)])
print('after: i =', i)

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

比较不可排序类型

作为python3的重大改进之一, 就是当开发者在试图比较两个不可排序的类型时, 系统会抛出TypeError的异常. 那么什么是不可排序变量呢? 举个例子, 当用liststr()比较, 就是一个不可排序类型的比较.

处理用户输入

在python中, 如果要处理用户的输入, 最常使用的方法是input(). 但是很多开发者不知道的是, 在python2当中, input()所返回的类型并不是固定的, 为了避免一些危险的操作出现, 有经验的python2开发会使用raw_input()而不是input(). 但是, 在python3中, input()的返回类型终于被固定了, 在python3的各个版本当中, inpit()返回的是一个str对象.

python2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
my_input = input('enter number: ')

>>> enter number: 123

type(my_input)

>>> <type 'int'>

my_input = raw_input('enter number: ')

>>> enter number: 123

type(my_input)

>>> <type 'str'>

pythnon3

1
2
3
4
5
6
7
my_input = input('enter number: ')

>>> enter number: 123

type(my_input)

>>> <type 'str'>