What’s New In Python 3.10
Important
Nice
Type Hints
PEP 604: New Type Union Operator
可以用|符号代替typing.Union
PEP 612: Parameter Specification Variables
ParamSpec
ParamSpec 用于表示一个可调用对象的参数规范, 适用用装饰器等特性.
Example
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec('P')
R = TypeVar('R')
def decorator(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"Calling {func} with args={args} and kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
Concatenate
与ParamSpec 配合使用, 通过连接多个参数和ParamSpec参数, 来标识对函数的操作, 比如区分被装饰的函数的参数和额外的参数.
在示例中, sum_threadsafe 的 numbers 参数被用于inner的参数
Example
from collections.abc import Callable
from threading import Lock
from typing import Concatenate, ParamSpec, TypeVar
P = ParamSpec('P')
R = TypeVar('R')
my_lock = Lock()
def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return f(my_lock,*args,**kwargs)
return inner
@with_lock
def sum_threadsafe(lock: Lock, numbers: list[float]) -> float:
with lock:
return sum(numbers)
sum_threadsafe([1.1, 2.2, 3.3])
PEP 613: TypeAlias
用来帮助区分类型别名和赋值
Example
from typing import TypeAlias
Vector: TypeAlias = list[int]
PEP 647: User-Defined Type Guards
其作用是告诉类型检查器:当这个返回值为 True 时,函数参数的类型就被指定为TypeGuard[T] 中的类型T
Example
from typing import TypeGuard
def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in val)
x: list[object] = ["a", "b", "c"]
if is_str_list(x):
print("All strings:", x)
else:
print("Not all strings")
PEP 634: Structural Pattern Matching
- 从上到下对
pattern进行求值并执行首个匹配动作. - 未匹配时存在
_则执行该动作 - 至多只执行一个动作.
- 可细分为以下几种模式
- literal
- literal and variable
- classes
- positional parameters
- Nested
- wildcard
- Guard
Summary
- 通过
Guard可在表达式中添加条件判断, 延伸了灵活性 - patterns 无法匹配迭代器
- 序列 patterns 无法匹配字符串
Example
Synatxmatch subject:
case <pattern_1>:
<action_1>
case <pattern_2>:
<action_2>
case <pattern_3>:
<action_3>
case _:
<action_wildcard>
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def location(point):
match point:
case Point(x=0, y=0):
print("Origin is the point's location.")
case Point(x=0, y=y):
print(f"Y={y} and the point is on the y-axis.")
case Point(x=x, y=0):
print(f"X={x} and the point is on the x-axis.")
case Point():
print("The point is located somewhere else on the plane.")
case _:
print("Not a point")
class Point:
__match_args__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def location(point):
match point:
case Point(0, 0):
print("Origin is the point's location.")
case Point(0, y=y):
print(f"Y={y} and the point is on the y-axis.")
case Point(x=x, y=y):
print(f"X={x} and Y={y}.")
case Point():
print("The point is located somewhere else on the plane.")
case _:
print("Not a point")
location(Point(0,0))
location(Point(0,1))
location(Point(1,1))
match points:
case []:
print("No points in the list.")
case [Point(0, 0)]:
print("The origin is the only point in the list.")
case [Point(x, y)]:
print(f"A single point {x}, {y} is in the list.")
case [Point(0, y1), Point(0, y2)]:
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
case _:
print("Something else is found in the list.")
match test_variable:
case ('warning', code, 40):
print("A warning has been received.")
case ('error', code, _):
print(f"An error {code} occurred.")
match point:
case Point(x, y) if x == y:
print(f"The point is located on the diagonal Y=X at {x}.")
case Point(x, y):
print(f"Point is not on the diagonal.")
Better error messages
异常显示定位具体原因更容易了
Example
beforeFile "example.py", line 3
some_other_code = foo()
^
SyntaxError: invalid syntax
File "example.py", line 1
expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
^
SyntaxError: '{' was never closed
Trivial
Parenthesized context managers
Example
with (
CtxManager1() as example1,
CtxManager2() as example2
):
PEP 626: Precise line numbers for debugging and other tools
PEP 626 brings more precise and reliable line numbers for debugging, profiling and coverage tools. Tracing events, with the correct line number, are generated for all lines of code executed and only for lines of code that are executed.
一个调试相关的交互性优化, 更好定位代码.
Optional EncodingWarning and encoding="locale" option
The default encoding of TextIOWrapper and open() is platform and locale dependent. Since UTF-8 is used on most Unix platforms, omitting encoding option when opening UTF-8 files (e.g. JSON, YAML, TOML, Markdown) is a very common bug. For example:
一个内置的警告功能,发现某些和编码相关的潜在的跨平台 bug
Others
int.bit_count返回二进制中 1 的数量- 对于
dict.keys()、dict.values()、dict.items()返回值添加一个只读的mapping属性 - zip 函数添加了
strict可选参数, 要求可变参数的长度保持一致 - 对于未实现的
__ipow__操作,会回退到__pow__和__rpow__ - 赋值表达式在集合字面量和集合推导式中不再需要添加括号, 如
{s:=(lambda x: x)(i) for i in range(5)} - 为函数添加
__builtins__属性以查找builtin对象, 初始化优先级从函数自身的__globals__["__builtins__"]再到模块的__builtins__进行复制 - 添加
aiteranext已对应异步的iternext函数 staticmethod可以像普通函数一样被调用from __future__ import annotations仅对pep526内的simple name生效- 类和模块的
__annotations__支持懒加载, 在首次访问时实现 - 在使用
from __future__ import annotations后, 注解中不允许出现yield,yield from,await以及命名表达式:= - 在使用
from __future__ import annotations后, 注解中不允许出现未绑定的变量和super()以及其他会改变符号处理的表达式 float('nan')和decimal.Decimal('NaN')的哈希值不再固定返回为0del __debug__现在会抛出SyntaxError而不是NameErrorSyntaxError包含end_lineno和end_offset属性
总结
- 可以用
|代替typing.Union了 staticmethod用起来更直观了- 异常提示更友好了