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的关系。
那么,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()。
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()返回的可迭代对象转为一个列表,返回的变量类型为列表。
1.2.6 dict
python2中的dict相关的keys()、values()、items()、zip()、map()、filter(),在python3中不再返回list对象,而返回迭代器。其可通过list强行转换。
同时,python3废弃has_key函数,统一使用in关键词。
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 其他
- Python3废弃apply函数
- Python3废弃异常StandardError,统一使用Exception
1.3 修改类差异
1.3.1 除法/
浮点数除法操作符/,在python2整数得整数、浮点得浮点数,而在python3中结果始终为浮点数。
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循环不会修改外部相同名称变量的值。
1.3.4 round函数
Python2中,round函数返回float类型值,在python3中返回int类型值
1.3.5 比较符
Python2中任意两个对象都可以比较,而在python3中仅同一数据类型的对象可以比较。
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 其它
其它性详细的对比可参考重要点进行介绍的博文,可以作为参考:
- python2与python3性能对比:https://blog.csdn.net/zw0pi8g5c1x/article/details/81187670
- python2/3 进程/线程效率对比:https://www.cplusplus.me/2887.html#256
- Python2和Python3对比实验设计:https://blog.csdn.net/weixin_34198881/article/details/87377252