python2与python3的功能性能对比

内容纲要

Python2将在2020年元旦正式停止官方支持,同时也有越来越多的python库不再支持python2。为了更好的保证工程运行的稳定性、安全性等,考虑要做Python的版本升级,为此对Python2和Python3的差异做了一个调研,主要从功能和性能两方面做了对比。

一、 功能

功能部分主要包括语法、工具包支持程度两方面。

1.1 核心类差异

1.1.1 编码与字符串

python2 python3
默认asscii 默认utf-8
第一行声明
#_*_coding:UTF-8_*_
声明可不加,加也可以(不使用utf-8时要声明
中文前加u,表示unicode 中文前可不加u,加也可以
str:编码后的字节序列
unicode:编码前的文本字符
str:编码后的 unicode 文本字符
bytes:编码前的字节序列

下面对比python2和python3对编码和解码的处理及str、unicode、bytes的关系。

file

file

file

file

那么,python3为什么更换了默认编码呢?

Python2默认ASCII编码方式,但是ASCII编码无法对中文等字符进行有效编码,因此在涉及到中文等其他字符的编码问题时,ASCII不仅无能为力,而且经常带来一些乱七八糟的错误,这也是Python2中经常出现编码错误的原因之一。

1.1.2 导入相对包

Python2中相对路径的import会导致标准库导入变得困难(想象一下,同一目录下有file.py,如何同时导入这个文件和标准库file)。Python3中这一点将被修改,如果还需要导入同一目录的文件必须使用绝对路径,否则只能使用相关导入的方式来进行导入。

例如,存在以下结构文件:

test/
    __init__.py
    aaa.py
    bbb.py
    ccc.py
场景 python2 python3
ccc.py中导入aaa.py import aaa from . import aaa
ccc.py中导入bbb.py中一个类 from bbb import BBB from . bbb import BBB
  • Python 2解释器会先在当前目录里搜索aaa.py,然后再去Python搜索路径(sys.path)里搜索
  • Python 3会直接在Python的搜索路径里寻找。如果你想要包里的一个模块导入包里的另外一个模块,需要显式地提供两个模块的相对路径。

1.1.3 新式类与经典类(旧式类)

新式类都从object继承,采用广度优先搜索继承方式,先深入继承树左侧查找,然后再返回,开始查找右侧;经典类不需要从object继承,采用深度优先搜索继承方式,先在水平方向查找,然后再向上查找。

# 经典类写法
class class_name:
    pass
# 新式类写法
class class_name(object):
    pass

Python2存在经典类和新式类,python3取消经典类和新式类之分,统一为新式类。

# py2默认经典类,只有显式继承object才是新式类
class Person(object): # 新式类写法
    pass
class Person(): # 经典类写法
    pass
class Person: # 经典类写法
    pass
# py3默认新式类,不必显式的继承object
class Person(object):
    pass
class Person():
    pass
class Person:
    pass
# 三种写法无区别,推荐第一种

下面使用python2,举例说明经典类和新式类继承方式的不同。

经典类的深度优先搜索继承方式

假设有4个类ABCD,D类继承于B类和C类,B类与C类继承于A类,代码如下:

class A: # 经典类
    def __init__(self):
        print('This is from A.')

class B(A):
    pass

class C(A):
    def __init__(self):
        print('This is from C.')

class D(B,C):
    pass

D()

现执行D()之后,结果如何呢?我们具体分析一下。

D类没有构造函数,所以从父类BC中继承,父类的继承顺序为从左往右,所以D类优先从B类中继承,B类没有构造函数,从A类中继承,而不是C类。若A类也没有,才会从C类中继承。(D -> B -> A -> C)这就是深度优先搜索继承的顺序,所以最终结果是This is from A.

新式类的广度优先搜索继承方式

class A(object): # 新式类
    def __init__(self):
        print('This is from A.')

class B(A):
    pass

class C(A):
    def __init__(self):
        print('This is from C.')

class D(B,C):
    pass

D()

D类没有构造函数,所以从父类BC中继承,父类的继承顺序为从左往右,所以D类优先从B类中继承,B类没有构造函数,从C类中继承,而不是A类。若C类也没有,才会从A类中继承。(D -> B -> C -> A)这就是广度优先搜索继承的顺序,所以最终结果是This is from C.

1.1.4 缩进

python2中,1个tab等价于8个space,所以在缩进中允许tab和space共存,这种等价机制会导致部分IDE使用存在问题。故python3对于缩进的使用要求更加严格了,1个tab只能找另外一个tab替代,不允许tab和space共存,如果共存会报错:TabError: inconsistent use of tabs and spaces in indentation.

1.2 废弃类差异

1.2.1 print

python3中print语句被废弃,统一使用print()。

file

file

1.2.2 exec

exec语句被python3废弃,统一使用exec函数。

python2 python3
exec codeString exec(codeString)
exec codeString in a_global_namespace exec(codeString, a_global_namespace)
exec codeString in a_global_namespace, a_local_namespace exec(codeString, a_global_namespace, a_local_namespace)

另,execfile语句被Python3废弃,推荐使用exec(open("./filename").read())。

1.2.3 不等号

Python3废弃不相等操作符"",统一使用"!="。

1.2.4 long

long整数类型被Python3废弃,统一使用int。

1.2.5 range

Python3废弃xrange函数,统一使用range。Python3中range的机制也进行修改并提高了大数据集生成效率。

Python2 range() 函数返回的是列表。

Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表。

Python3 list() 函数是对象迭代器,可以把range()返回的可迭代对象转为一个列表,返回的变量类型为列表。

file
file

1.2.6 dict

python2中的dict相关的keys()、values()、items()、zip()、map()、filter(),在python3中不再返回list对象,而返回迭代器。其可通过list强行转换。

同时,python3废弃has_key函数,统一使用in关键词。

file
file

1.2.7 next

迭代器iterator的next()函数被Python3废弃,统一使用next(iterator)。

python2 python3
IteratorA.next() next(IteratorA)
a_function_that_returns_an_iterator().next() next(a_function_that_returns_an_iterator())
class A:
 def next(self):
  pass
class A:
 def next(self):
  pass
class A:
 def next(self, x, y):
  pass
不变
next = 42
 for an_iterator in a_sequence_of_iterators:
  an_iterator.next()
next = 42
 for an_iterator in a_sequence_of_iterators:
  an_iterator.next()

1.2.8 input

Python3废弃raw_input函数,统一使用input函数。

1.2.9 file

file函数被Python3废弃,统一使用open来处理文件,可以通过io.IOBase检查文件类型

1.2.10 其他

  1. Python3废弃apply函数
  2. Python3废弃异常StandardError,统一使用Exception

1.3 修改类差异

1.3.1 除法/

浮点数除法操作符/,在python2整数得整数、浮点得浮点数,而在python3中结果始终为浮点数。

file
file

1.3.2 异常

  python2 python3
raise raise ImportError,‘msg’
raise ImportError("msg")
raise ImportError("msg")
  raise MyException,'error message', a_traceback raise MyException('error message').with_traceback(a_traceback)
  raise MyException 不变
  raise 'error message' 不变
try...except try:
 -----
expect Exception,e:
 -----
try:
 -----
expect Exception as e:
 -----
  try:
 import mymodule
expect ImportError:
 pass
不变

1.3.3 for循环中变量值

Python2中for循环会修改外部相同名称变量的值,python3修改了这一问题,for循环不会修改外部相同名称变量的值。

file
file

1.3.4 round函数

Python2中,round函数返回float类型值,在python3中返回int类型值

file
file

1.3.5 比较符

Python2中任意两个对象都可以比较,而在python3中仅同一数据类型的对象可以比较。

file
file

1.4 新增类差异

python3新增类在python2中都不支持,对其官网中有详细的说明,本期不做重点介绍。

二、 性能

对比Python2.7.3与Python3.7.2在磁盘IO及CPU上的差异。

2.1 磁盘IO

使用写文件操作进行测试,循环写入100000次。

python2耗时7s,python3耗时12s。相比python2,python3慢71%。

2.2 CPU

使用累加运算和累乘进行测试。

累加时,python2耗时22s,python3耗时25s;累乘时,python2耗时7s,python3耗时6s;整体上相比python2,python3慢14%。

2.3 其它

其它性详细的对比可参考重要点进行介绍的博文,可以作为参考:

发表评论

邮箱地址不会被公开。 必填项已用*标注