Skip to content

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_threadsafenumbers 参数被用于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

  1. 从上到下对 pattern 进行求值并执行首个匹配动作.
  2. 未匹配时存在 _ 则执行该动作
  3. 至多只执行一个动作.
  4. 可细分为以下几种模式
    1. literal
    2. literal and variable
    3. classes
    4. positional parameters
    5. Nested
    6. wildcard
    7. Guard

Summary

  1. 通过 Guard 可在表达式中添加条件判断, 延伸了灵活性
  2. patterns 无法匹配迭代器
  3. 序列 patterns 无法匹配字符串
Example Synatx
match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>
match to a literal
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"
Patterns with a literal and variable
# 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")
Patterns and classes
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")
Patterns with positional parameters
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))
Nested patterns
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.")
Complex patterns and the wildcard
match test_variable:
    case ('warning', code, 40):
        print("A warning has been received.")
    case ('error', code, _):
        print(f"An error {code} occurred.")
Guard
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 before
File "example.py", line 3
    some_other_code = foo()
                    ^
SyntaxError: invalid syntax
after
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

  1. int.bit_count 返回二进制中 1 的数量
  2. 对于dict.keys()dict.values()dict.items()返回值添加一个只读的mapping属性
  3. zip 函数添加了strict可选参数, 要求可变参数的长度保持一致
  4. 对于未实现的__ipow__操作,会回退到__pow____rpow__
  5. 赋值表达式在集合字面量和集合推导式中不再需要添加括号, 如{s:=(lambda x: x)(i) for i in range(5)}
  6. 为函数添加__builtins__属性以查找builtin对象, 初始化优先级从函数自身的__globals__["__builtins__"] 再到模块的__builtins__ 进行复制
  7. 添加aiter anext 已对应异步的iter next函数
  8. staticmethod 可以像普通函数一样被调用
  9. from __future__ import annotations 仅对 pep526 内的simple name生效
  10. 类和模块的__annotations__ 支持懒加载, 在首次访问时实现
  11. 在使用 from __future__ import annotations 后, 注解中不允许出现 yield, yield from, await以及命名表达式:=
  12. 在使用 from __future__ import annotations 后, 注解中不允许出现未绑定的变量和super() 以及其他会改变符号处理的表达式
  13. float('nan')decimal.Decimal('NaN') 的哈希值不再固定返回为 0
  14. del __debug__ 现在会抛出 SyntaxError 而不是 NameError
  15. SyntaxError 包含end_linenoend_offset 属性

总结

  1. 可以用 | 代替 typing.Union
  2. staticmethod 用起来更直观了
  3. 异常提示更友好了