转自:数据STUDIO
刚刚发布的 Python 3.13 继续挑战了效率和优雅的极限。
除了讨论已久的令人兴奋的自由线程模式 和 Just-In-Time 编译器之外,吸引我的还有类型系统的新改进。
在早期版本引入的强大类型系统基础上,Python 3.13 将引入七个新的类型特性,有望提高代码的可靠性和开发人员的工作效率。
在本文中,我们将尝试这些令人兴奋的新特性,并探索它们如何简化我们的代码并将我们的编程实践提升到新的高度。
本文中的所有代码片段都是在 Python 3.13.0rc2 的最新发布版本上测试的,该版本是 Python 3.13 的最终发布预览版。3.13.0 的正式版本于 2024 年 10 月 1 日星期二发布。
1. ReadOnly类型
将项目定义为只读
新的 ReadOnly
类型,顾名思义,是一种特殊的类型构造,用于将 TypedDict
中的项目标记为只读。
from typing import TypedDict, ReadOnly
class Leader(TypedDict):
name: ReadOnly[str]
age: int
author: Leader = {'name': 'Yang Zhou', 'age': 30}
author['age'] = 31 # no problem to change
author['name'] = 'Yang' # Type check error: "name" is read-only
上面的代码展示了它的用法。由于我们将 name
属性定义为 ReadOnly[str]
类型,因此更改其值将在集成开发环境或其他静态类型检查工具中调用类型不一致提示。
注意:“ReadOnly” 类型只能在 “TypedDict” 中使用。
如果你喜欢更简单的 TypedDict
定义方式,也可以使用 ReadOnly
类型:
from typing import TypedDict, ReadOnly
Leader = TypedDict("Leader", {"name": ReadOnly[str], "age": int})
author: Leader = {'name': 'Yang Zhou', 'age': 30}
author['age'] = 31 # no problem to change
author['name'] = 'Tim' # Type check error: "name" is read-only
2. @warnings.deprecated
新的装饰器,用于指示对象已被弃用
好的软件会不断改进。这不仅意味着添加新内容,还意味着删除过时的内容。
然而,在下一个新版本中直接删除函数或类的做法对用户来说不够友好。我们不应该这样做。
如果您严格遵守 Python 的官方文档。你会发现 Python 逐步删除无用对象的策略才是行业标准:
将相对的对象标记为废弃对象,提前告知开发者将来会删除哪些对象。但这些对象仍然可以在接下来的几个版本中使用。 几个版本之后,完全知情的对象将从 Python 的最新版本中完全移除。
Python 3.13 为我们提供了一种更方便的方法来标记被废弃的对象 - 一个新的 decorator,名为 @warings.deprecated
。
只要一个对象配备了这个装饰器,静态类型检查工具或集成开发环境就会提醒我们使用已废弃对象。
例如,我在下面的程序中使用了一个已废弃的对象,集成开发环境 (PyCharm) 会通过明显的删除线提醒我:
简单易用,意义非凡,这又是一个经典的 Pythonic 设计!
3. TypeIs
让类型缩小更容易
新的 “TypeIs” 概念旨在 “类型缩小”(type narrowing),其官方文档中描述“类型缩小”是静态类型检查器用来确定程序代码流中表达式的更精确类型的一种技术。
而我们的应用代码中使用它的几率并不高。但我们需要了解它是什么:
简而言之,形式
def foo(arg: TypeA) -> TypeIs[TypeB]: ...
,意味着如果foo(arg)
返回True
,则arg
是TypeB
的实例,如果返回False
,则它不是TypeB
的实例。
4. is_protocol
快速检查类是否属于协议类型的新函数
这个新函数 is_protocol
是检查对象是否为 Protocol
类型的便捷方法。
我们只需从 typing
模块中导入该函数并直接使用即可:
from typing import is_protocol, Protocol
class PersonProto(Protocol):
name: str
age: int
print(is_protocol(PersonProto))
# True
print(is_protocol(int))
# False
5. get_protocol_members
返回协议成员集合函数
新的 get_protocol_members()
函数用于快速获取一个 Protocol
类型的所有项目名称。它返回一个包含所有名称的 frozenset
:
from typing import Protocol, get_protocol_members
类 PersonProto(Protocol):
name: str
age: int
print(get_protocol_members(PersonProto))
# frozenset({'age', 'name'})
6. TypeVar、ParamSpec 和 TypeVarTuple 的默认类型
在 Python 3.13 中,类型参数 (typing.TypeVar、typing.ParamSpec 和 typing.TypeVarTuple 现在支持默认类型。其用法非常简单。
例如,下面的代码显示了如何轻松地将默认类型设置为 TypeVar
:
from typing import TypeVar
T = TypeVar("T", default=int) # This means that if no type is specified T is int
print(T.has_default())
# True
S = TypeVar("S")
print(S.has_default())
# False
Python 3.13 还添加了 has_default()
函数来检查 TypeVar
是否有缺省类型。
7. NoDefault
表示没有默认值
除了为一些新的类型参数提供默认支持外,typing
模块还提供了一个名为 NoDefault
的新对象,用于指示类型参数没有默认值。
from typing import TypeVar, NoDefault
T = TypeVar("T")
print(T.__default__ is NoDefault)
# True
S = TypeVar("S", default=None)
print(S.__default__ is NoDefault)
# False
正如上面的代码所证明的,如果没有类型被设置为默认值,那么 TypeVar
的 __default__
属性将是 NoDefault
。但如果它的默认值是 None
,它仍然有默认值。
性能改进和方法删除
Python 3.13 官方文档中提到,通过移除对 re
和 contextlib
的依赖,typing
模块的导入时间减少了大约三分之一。
我们还需要注意的是,从这个新 Python 版本开始,一些与键入相关的东西将被移除:
删除
typing.io
和typing.re
命名空间,它们自 Python 3.8 起已被弃用。这些命名空间中的项可以直接从typing
模块导入。删除创建
TypedDict
类型的关键字参数方法,该方法在 Python 3.11 中已被弃用。