文章目录
-
- Python2 与 Python3
- Python2 系列
- Python3 系列
-
- Python3.4
- Python3.5
- Python3.6
- Python3.7
- Python3.8
- Python3.9
- Python3.10
- Python3.11
Python 的版本主要分为 2.×
、 3.×
两个系列。
- Python3 计划每年发布一个新的子版本,每次只增加两三种新语法。
- 使用时当然选择越新的 Python 版本越好,版本越老的代码越难维护。
- 维护老版本的代码时,需要了解各版本之间的主要差异。有时看到一些代码的语法特点,可以大致猜出它是什么版本。
Python2 与 Python3
- 从 Python2 到 Python3 是一个大版本升级,有很多不向下兼容的差异,导致很多 Python2 的代码不能被 Python3 解释器运行,或者反之。
- 2020 年开始,Python 官方停止对 Python2 的维护,所有老代码都会超过保质期。
差异点 | Python2 | Python3 |
---|---|---|
输出方式 | 用 print 关键字,比如 print "Hello" |
用 print()函数,比如 print("Hello") |
输入方式 | 用 raw_input()函数 | 用 input()函数 |
字符串的编码格式 | 默认采用 ASCII | 默认采用 Unicode |
格式化字符串的方式 | 用 % ,比如 "Hello, %s" % ("World") |
用 format()函数,比如 "Hello, {}".format('World') |
源文件的编码格式 | 默认采用 ASCII ,因此使用中文时要在源文件开头加上一行 # -*- coding: utf-8 -*- |
默认采用 uft-8 |
… | … | … |
Python2 系列
- Python2.0 :于 2000 年发布https://www.python.org/downloads/release/2.0/
- Python2.1 ~ Python2.6 略
- Python2.7 :于 2010 年发布https://www.python.org/downloads/release/python-270/ ,这是 Python2 的最后一个子版本
Python3 系列
- Python3.0 :于 2008 年发布https://www.python.org/download/releases/3.0/
- Python3.1 ~ Python3.3 略
Python3.4
-
于 2014 年发布:https://www.python.org/downloads/release/python-340/
-
采用
pip
作为 Python 包的默认安装方式。 -
增加标准库
pathlib
,用于按面向对象的方式操作文件路径。如下:>>> from pathlib import Path >>> p = Path('/root/test/1.py') >>> p.name '1.py' >>> p.suffix '.py' >>> p.exists() False
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
增加标准库
enum
,用于定义枚举类。如下:>>> from enum import Enum >>> >>> class Test(Enum): ... a = 1 ... b = 2 ... c = 3 ... >>> Test.a <Test.a: 1> >>> Test['a'] # 可按名字索引 <Test.a: 1> >>> list(Test) # 可迭代 [<Test.a: 1>, <Test.b: 2>, <Test.c: 3>]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
-
增加标准库
asyncio
,用于实现异步 IO 。 -
增加标准库
statistics
,提供了求平均值、中位数、方差等运算的函数。 -
增加标准库
tracemalloc
,用于跟踪内存分配的情况,方便调试。
Python3.5
-
于 2015 年发布:https://www.python.org/downloads/release/python-350/
-
扩展了迭代拆包运算符
*
、字典拆包运算符**
的语法。- 可以在元组、列表、集合、字典表达式中使用:
>>> *range(4) SyntaxError: can't use starred expression here >>> *range(4), 4 (0, 1, 2, 3, 4) >>> [*range(4), 4] [0, 1, 2, 3, 4] >>> {'a': 1, **{'b': 2}} {'a': 1, 'b': 2}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 可以同时多次使用:
>>> print(*[1], *[2], *{'c': 3}) 1 2 c >>> dict(**{'a': 1}, **{'b': 2}) {'a': 1, 'b': 2}
- 1
- 2
- 3
- 4
- 可以在元组、列表、集合、字典表达式中使用:
-
增加语法:使用 Python2 风格的百分号
%
来格式化字符串。>>> '%a' % 3.14 '3.14' >>> b'%a' % 3.14 b'3.14'
- 1
- 2
- 3
- 4
-
增加关键字
async
、await
,用于定义协程:async def read_db(db): data = await db.fetch('SELECT ...')
- 1
- 2
-
增加语法:类型注释(type annotations)。
- 对于函数,可以用冒号
:
添加形参的注释,用->
添加返回值的注释。这些注释会存储在函数的__annotations__
属性中:>>> def fun1(a, b: "字符串或 None", c: int = 0) -> int: ... pass ... >>> fun1.__annotations__ {'b': '字符串或 None', 'c': <class 'int'>, 'return': <class 'int'>}
- 1
- 2
- 3
- 4
- 5
- 对于变量,可以用冒号
:
添加注释:>>> x : int # 该语句可以执行,但并不会创建变量 x >>> x NameError: name 'x' is not defined >>> x : int = 1 # 添加类型注释并赋值 >>> x 1
- 1
- 2
- 3
- 4
- 5
- 6
- 类型注释可以是任意可执行的表达式。例如:
>>> x : int # 该注释为一个类型,不会强制类型检查,但可以被 IDE 静态类型检查 >>> x : 'Hello' # 该注释为一个 str 值 >>> x : print('Hello') # 该注释为一个语句 Hello
- 1
- 2
- 3
- 4
- 对于函数,可以用冒号
-
增加标准库
typing
,定义了一些类型,常用于类型注释。 -
增加标准库
zipapp
,用于将 Python 脚本打包成可执行的归档文件,扩展名为 .pyz 。
Python3.6
-
于 2016 年发布:https://www.python.org/downloads/release/python-360/
-
dict 中的元素以前是按 key 顺序存储:
>>> {2:'', 1:''} {1: '', 2: ''}
- 1
- 2
现在会按插入顺序存储:
>>> {2:'', 1:''} {2: '', 1: ''}
- 1
- 2
-
增加语法:在数字中插入下划线作为分隔符,提高可读性。
>>> 1_000_111_000 1000111000 >>> '{:_}'.format(1000000) # 格式化字符串时也可输出下划线 '1_000_000'
- 1
- 2
- 3
- 4
-
增加语法:给字符串加上前缀 f 之后,就会执行花括号 {} 内的语句。
>>> a = 1 >>> f'a={a}' 'a=1' >>> f'{int(1) + 2}' '3'
- 1
- 2
- 3
- 4
- 5
-
增加一种定义元类的方法:给类定义
__init_subclass__()
方法,用于初始化子类。如下:class TestBase: subclasses = [] def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) cls.subclasses.append(cls)
- 1
- 2
- 3
- 4
- 5
- 6
-
增加标准库
secrets
,用于生成安全的随机数。- 原有的
random
模块生成的随机数可能被预测,不适合用作密码、加密密钥,否则存在安全隐患。
- 原有的
Python3.7
- 于 2018 年发布:https://www.python.org/downloads/release/python-370/
Python3.8
-
于 2019 年发布:https://www.python.org/downloads/release/python-380/
-
增加语法:赋值表达式,用于给表达式中的变量赋值。
if a := input(): # 相当于先执行 a = input(); 再执行 if a: print(a)
- 1
- 2
>>> (a := 0) + 1 1 >>> a 0
- 1
- 2
- 3
- 4
-
增加语法:定义函数时,在正斜杆 / 之前的参数都会被视作位置参数。
>>> def fun1(a, b, c=0, /, *args, **kwargs): ... pass ... >>> fun1(1, 2, 3) >>> fun1(1, 2)
- 1
- 2
- 3
- 4
- 5
-
增加语法:在 f-string 中用
{var}
或{var=}
的格式插入变量的值,比 str.format() 更方便。>>> f'{a}' NameError: name 'a' is not defined # 读取不存在的变量时会报错 >>> a = 1 >>> f'{a}' '1' >>> f'{a=}' 'a=1' >>> f'DEBUG: {id(1)=}' # 也可插入函数的返回值,会自动转换成 str 类型 'DEBUG: id(1)=140298296357120'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-
支持在 finally 语句块中使用 continue 关键字。
-
multiprocessing 模块增加一个 SharedMemory 类,用于创建进程之间的共享内存。
Python3.9
-
于 2020 年发布:https://www.python.org/downloads/release/python-390/
-
dict 类增加合并运算符
|
、更新运算符|=
。- 以前合并字典的方式主要有两种:
{**d1 , **d2} d1.update(d2) # update() 方法会改变字典的内容
- 1
- 2
例如:
>>> d1 = {1: 'A'} >>> d2 = {2: 'B'} >>> {**d1, **d2} {1: 'A', 2: 'B'} >>> d1.update(d2) >>> d1 {1: 'A', 2: 'B'}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 现在可以通过运算符合并字典:
>>> d1 = {1: 'A'} >>> d2 = {2: 'B'} >>> d1 | d2 # 相当于 {**d1 , **d2} {1: 'A', 2: 'B'} >>> d1 |= d2 # 相当于 d1.update(d2) >>> d1 {1: 'A', 2: 'B'}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 以前合并字典的方式主要有两种:
-
str 类增加两个方法
removeprefix()
、removesuffix()
。如下:>>> s = 'Hello' >>> s.rstrip('lo') # 以前的 rstrip() 方法会删除右侧所有匹配的单个字符 'He' >>> s.removesuffix('lo') # 现在的 removesuffix() 方法只会删除匹配的子字符串 'Hel'
- 1
- 2
- 3
- 4
- 5
其原理为:
def removeprefix(self: str, prefix: str, /) -> str: if self.startswith(prefix): return self[len(prefix):] else: return self[:]
- 1
- 2
- 3
- 4
- 5
-
支持将大部分内置类型的类名用作函数实参,便于声明注释。如下:
>>> T = type[int] >>> T type[int] >>> list[str] == list[str] True >>> tuple[int, ...] tuple[int, ...] >>> def fun1(x: dict[str, list[int]]): ... pass ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Python3.10
-
于 2021 年发布:https://www.python.org/downloads/release/python-3100/
-
增加语法:match-case 模式匹配。格式如下:
match <expression> case <pattern>: <block> case <pattern>: <block> ...
- 1
- 2
- 3
- 4
- 5
- 6
执行时,会从上到下依次将 expression 与每个 case pattern 比较。如果匹配,则执行该 case block ,并结束 match-case 语句块。
pattern 有多种语法,比如模糊匹配、绑定变量, 因此比 C 语言的 switch-case 语句有更多功能。 -
增加语法:用
|
运算符连接多个类型,表示 Union 类型。>>> int | str # 相当于 typing.Union[int, str] >>> isinstance(1, int|str) # 相当于 isinstance(1, (int, str)) 和 isinstance(1, typing.Union[int, str]) True >>> issubclass(set, int|str) False
- 1
- 2
- 3
- 4
- 5
-
调用 open() 函数时,允许传入参数
encoding='locale'
,等价于encoding=None
,表示采用当前平台的默认编码格式。
Python3.11
-
于 2022 年发布:https://www.python.org/downloads/release/python-3110/
-
CPython 解释器优化了加载模块、调用函数等操作,使得 Python3.11 比 Python3.10 的启动速度、运行速度快了 10%~60% 。
-
增加了标准异常类型 BaseExceptionGroup、ExceptionGroup ,用于将多个异常打包为一组。异常组只能用语法 except* 捕捉。
- 例:
>>> try: ... raise ExceptionGroup('异常组1', ([ # 创建异常组时,需要传入组名,和一组异常对象。支持嵌套异常组 ... KeyError('invalid key'), ... TypeError('invalid type'), ... ValueError('invalid value'), ... ])) ... except* TypeError as e: ... print('A') ... except* (TypeError, ValueError) as e: ... print('B') ... A B + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | ExceptionGroup: 异常组1 (1 sub-exception) +-+---------------- 1 ---------------- | KeyError: 'invalid key' +------------------------------------
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- try 一个异常组时,会从上往下检查各个 except* 。
- 每个 except* 可以指定一个或多个异常,只要任一异常包含于异常组,则执行该 except* 子句。然后从异常组删除已捕捉的异常,继续匹配之后的 except* 。
- 一个普通异常最多触发一个 except 子句,而一个异常组可能触发多个 except* 子句。
- 如果 except* 没有捕捉异常组中的全部异常,则剩下的异常会被抛出。
- 例:
-
打印 tracebacks 异常信息时,更准确地指出引发异常的代码位置。
- 以前,Python 解释器在执行 Python 程序时,只知道当前执行的是哪条字节码。现在,用两个 uint8_t 数据类型记录每条字节码的起始偏移量、结束偏移量,从而能根据已执行的偏移量,判断目前执行到哪条字节码的哪个位置。
- 例如编写一个 test.py 脚本:
1 + 2/0 + 3
- 1
然后用 python 命令执行:
[root@CentOS ~]# python test.py Traceback (most recent call last): File "/root/test.py", line 1, in <module> 1 + 2/0 + 3 ~^~ ZeroDivisionError: division by zero
- 1
- 2
- 3
- 4
- 5
- 6
倒数第二行信息是 Python3.11 才能打印的,指出这行代码的哪个位置引发了异常。
- 缺点:
- pyc 文件、内存中的字节码体积会增大 22% 。不过一般 Python 程序的字节码总共只有几十 MB ,影响小。
- 用 Python 解释器交互式编程时,没有该功能。