Python 3.5 之后的新特性
Python 自 3.5 版本起,至当前 3.11 版本为止,变化相当大,引入了众多的新特性,了解这些变化对编写兼容性代码尤为重要。本文整理的一些版本的重要变化。
Python 3.5
此版本最早发布于 2015 年,最终版本为 3.5.10 ,发布于 2020 年。
[PEP 492] 使用 async
/await
语法实现协程
见 PEP 492 。
此特性是 3.5 版本最重大的变化。自此,Python 支持 async
和 await
语法,用于代替 @asyncio.coroutine
装饰器和 yield from
语句。
[PEP 448] 通用化额外的解包操作
见 PEP 448 。
解包即 *
和 **
操作,曾在传递列表和字典类型的函数参数时使用。新特性将这一操作推广到了其他使用场景。
python*range(4), 4 # (0, 1, 2, 3, 4)
[*range(4), 4] # [0, 1, 2, 3, 4]
{*range(4), 4} # {0, 1, 2, 3, 4}
{'x': 1, **{'y': 2}} # {'x': 1, 'y': 2}
[PEP 484] 类型提示
见 PEP 484 。
支持函数和方法的参数及返回值的类型提示:
pythondef greeting(name: str) -> str:
return 'Hello ' + name
还有对泛型的支持:
pythonfrom typing import Mapping, Set
def notify_by_email(employees: Set[Employee], overrides: Mapping[str, str]) -> None:
pass
pythonfrom typing import Sequence, TypeVar
T = TypeVar('T') # Declare type variable
def first(l: Sequence[T]) -> T: # Generic function
return l[0]
其他
Python 3.6
此版本最早发布于 2016 年,最终版本为 3.6.15 ,发布于 2021 年。
[PEP 487] 魔术方法 __init_subclass__
和 __set_name__
见 PEP 487 。
当声明或创建新的子类时,基类的 __init_subclass__
方法将会被调用:
pythonclass PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
当一个类被创建时,type.__new__()
会扫描类变量并对其中带有 __set_name__
方法的对象执行回调。
pythonclass A:
def __set_name__(self, owner, name):
print(owner, name)
class B:
a = A() # 隐含调用 a.__set_name__(B, 'a')
[PEP 498] 格式化字符串字面值
见 PEP 498 。
新增字符串前缀 f
,实现类似 str.format()
的效果。
pythonname = "Fred"
f"He said his name is {name}."
width = 10
precision = 4
value = decimal.Decimal("12.34567")
f"result: {value:{width}.{precision}}" # nested fields
[PEP 526] 变量注解语法
见 PEP 526 。
继 PEP 484 支持函数参数和返回值的类型提示后,此 PEP 增进了对变量类型的声明支持。
pythonprimes: List[int] = []
captain: str # Note: no initial value!
class Starship:
stats: Dict[str, int] = {}
[PEP 468] 保持 **kwargs
传参时的顺序
见 PEP 468 。
pythonkwargs = {'z': 1, 'a': 2, 'x': 3, 'c': 4}
def ordered_kwargs(**kwargs):
print(kwargs)
ordered_kwargs(**kwargs) # {'z': 1, 'a': 2, 'x': 3, 'c': 4}
字典对象会保持插入时的顺序
也就是说默认的 dict()
对象已经具有 OrderedDict
的特性。此外,类属性也会保持定义时的顺序。
其他
- [PEP 519] 添加文件系统路径协议
- [PEP 528] Windows 文件系统和控制台编码使用 UTF-8
- [PEP 506] 新增模块
secrets
- [PEP 525] 异步生成器
- [PEP 530] 异步推导式:在推导式中使用
await
和async for
- [PEP 515] 数字字面值可加入下划线
python
1_000_000_000_000_000 # 1000000000000000 0x_FF_FF_FF_FF # 4294967295
Python 3.7
此版本最早发布于 2018 年,最新版本为 3.7.15 ,发布于 2022 年。
[PEP 562] 模块的 __getattr__
和 __dir__
见 PEP 562 。
允许在模块上定义 __getattr__()
并且当以其他方式找不到某个模块属性时将会调用它。 还可以在模块上定义 __dir__()
。
[PEP 557] 新增模块 dataclasses
见 PEP 557 。
提供一个类装饰器,可以为类自动添加诸如 __init__()
、 __repr__()
、 __eq__()
、 __hash__()
等魔术方法。
pythonimport dataclasses
@dataclasses.dataclass
class Point:
x: float
y: float
z: float = 0.0
p = Point(1.5, 2.5)
print(p) # produces "Point(x=1.5, y=2.5, z=0.0)"
[PEP 567] 新增模块 contextvars
见 PEP 567 。
用于异步和并发环境中管理上下文变量。可以替代 threading.local()
。
其他
- 新增模块
importlib.resources
- [PEP 553] 新增内建函数
breakpoint()
- [PEP 564] time 模块支持纳秒精度
Python 3.8
此版本最早发布于 2019 年,最新版本为 3.8.15 ,发布于 2022 年。
[PEP 572] 赋值表达式
见 PEP 572 。
新增 :=
赋值表达式语法,表达式将右值赋予左值并返回该值。
pythonif (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
[PEP 570] 仅限位置形参
见 PEP 570 。
函数参数中的 /
左边的参数为位置参数,不能作为关键字参数使用;而 *
右边的参数必须作为关键字参数使用。
pythondef f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
# valid
f(10, 20, 30, d=40, e=50, f=60)
# invalid
f(10, b=20, c=30, d=40, e=50, f=60) # b cannot be a keyword argument
f(10, 20, 30, 40, 50, f=60) # e must be a keyword argument
其他
- 格式化字符串支持文档化:
f'{expr=}'
Python 3.9
此版本最早发布于 2020 年,最新版本为 3.9.15 ,发布于 2022 年。
[PEP 584] 字典合并操作符 |
见 PEP 584 。
pythond = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e # {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
e | d # {'cheese': 3, 'aardvark': 'Ethel', 'spam': 1, 'eggs': 2}
[PEP 616] 字符串新增 removeprefix()
和 removesuffix()
方法
见 PEP 616 。
pythondef strip_quotes(text):
return text.removeprefix('"').removesuffix('"')
其他
Python 3.10
此版本最早发布于 2021 年,最新版本为 3.10.8 ,发布于 2022 年。
[PEP 617] 带括号的上下文管理器
见 PEP 617 。
此特性在 3.9 版本时已经存在,但是在 3.10 版本时可以正式使用。with
语句括号内支持多行表达式:
pythonwith (
open("a_really_long_foo") as foo,
open("a_really_long_baz") as baz,
open("a_really_long_bar") as bar
):
pass
[PEP 604] 允许 X | Y 形式的联合类型写法
见 PEP 604 。
pythonisinstance(5, int | str)
def f(list: List[int | str], param: int | None) -> float | str:
pass
int | str == typing.Union[int, str] # true
[PEP 634] 结构模式匹配
见 PEP 634 、 PEP 635 、 PEP 636 。
新增 match...case
语法。
pythoncommand = 'drop doc1 doc2 doc3'
match command.split():
case ["quit"]: pass
case ["go", direction]: pass
case ["drop", *objects]: pass
case _:
print(f"Sorry, I couldn't understand {command!r}")
其他
- [PEP 626] 在调试和其他工具中使用精确的行号
- [PEP 632] 弃用
distutils
模块 - [PEP 647] 用户定义的类型检查函数
typing.TypeGuard
- [PEP 612] 参数规范变量
typing.ParamSpec
- [PEP 613] 显式类型别名
typing.TypeAlias
Python 3.11
此版本今年刚发布 3.11.0 版本。
[PEP 654] ExceptionGroup
和 except*
见 PEP 654 。
ExceptionGroup
可以包含多个异常。except*
可以用来捕获 ExceptionGroup
中包含的异常。
pythontry:
raise ExceptionGroup('error', [TypeError(1), ValueError(2)])
except* TypeError as e:
print(f'*TypeError: {e!r}')
except* ValueError as e:
print(f'*ValueError: {e!r}')
[PEP 678] 新增方法 BaseException.add_note()
见 PEP 678 。
可以为异常添加字符串类型的 note ,并可以通过 __notes__
属性读取。
pythonexc = ValueError()
exc.add_note('note1')
exc.add_note('note2')
exc.add_note('note3')
print(exc.__notes__) # ['note1', 'note2', 'note3']
其他
总结
迄今为止,asyncio
模块已经相当完善了,以后的网络开发可以完全基于异步和协程。最近几次版本升级,更新重点还是在 typing
模块。感觉 Python 的泛型还不够完善,且过于复杂。typing
模块还不够稳定,不建议深度使用。千呼万唤的模式匹配语法终于在 3.10 里实现了,以后不用再写可悲的 if
嵌套。