惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
P
Privacy International News Feed
Security Latest
Security Latest
Spread Privacy
Spread Privacy
NISL@THU
NISL@THU
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Last Watchdog
The Last Watchdog
S
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Cyberwarzone
Cyberwarzone
C
Cyber Attacks, Cyber Crime and Cyber Security
G
GRAHAM CLULEY
A
Arctic Wolf
博客园 - 聂微东
I
Intezer
腾讯CDC
罗磊的独立博客
T
Tailwind CSS Blog
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
P
Palo Alto Networks Blog
Simon Willison's Weblog
Simon Willison's Weblog
W
WeLiveSecurity
N
News and Events Feed by Topic
SecWiki News
SecWiki News
S
Security Affairs
T
Threat Research - Cisco Blogs
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
T
Troy Hunt's Blog
N
News and Events Feed by Topic
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
酷 壳 – CoolShell
酷 壳 – CoolShell
M
MIT News - Artificial intelligence
I
InfoQ
Hacker News: Ask HN
Hacker News: Ask HN
T
The Blog of Author Tim Ferriss
Schneier on Security
Schneier on Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
D
Darknet – Hacking Tools, Hacker News & Cyber Security

博客园 - 赵青青

AI模型Claude的Haiku、Sonnet、Opus 怎么选? obsidian(md笔记管理)使用实践 python中可变参数与装饰器的例子 C++ lambda 表达式 3ds max的obj文件格式说明 CPython调试和性能分析 Python3虚拟机和对象 vs编译cpp时设置排除项 与ChatGPT的对话在windows上获取mac地址 Python311新特性-特化指令specializing adaptive interpreter-typing-asyncio 最小体积拉取git仓库并保持可更新 unity .net8 suppot comming pycharm一些减少代码warning的拼写检查设置 JavaScript速查表 Python cheatsheet 速查表 DirectX9(D3D9)游戏开发:高光时刻录制和共享纹理的踩坑 FFmpeg在游戏视频录制中的应用:画质与文件大小的综合比较 c++中字符串之string和char IMGUI快速入门
Python3类型安全type hint
赵青青 · 2024-12-15 · via 博客园 - 赵青青

type hint大纲

  1. 什么是类型安全以及我们为什么需要类型安全

  2. 如何使用Python3的type hint

  3. 老项目如何逐步引入type hint

  4. Type hint在运行时的应用

疑问

type hint在编译时会被去掉吧?

是的,Python的类型提示(Type Hints)只是一种语法糖,它们不会影响Python代码的运行。类型提示在运行时并不会进行类型检查,也不会影响代码的性能。它们主要是用来帮助程序员理解函数期望的输入和输出类型,以及提供给静态类型检查工具和IDE使用,以帮助找出潜在的错误。

类型标注能让我的程序运行得更快吗?

不会,Python的类型标注(Type Hints)并不会让你的程序运行得更快。类型标注在Python中主要是用于静态类型检查,以及提高代码的可读性和可维护性。它们在运行时并不会进行任何类型检查,也不会改变Python的动态类型特性,因此不会影响程序的运行速度。如果你想提高Python程序的运行速度,可以考虑使用如Cython、PyPy等工具,或者优化你的算法和数据结构。

为什么新的语言大部分都选择了类型在变量名的后面?

//Rust:
fn greeting(name: &str) -> String {
}

//Go:
func greeting(name string) string {
}

//Typescript:
function greeting(name: string): string {
}

python3的type check

Literal 在Python3中,字面量(Literal)是指在代码中直接使用的特定值。字面量可以是数字字面量,字符串字面量,布尔字面量,特殊字面量,或者容器字面量

Autocomplete 自动补全

添加type hint的位置
• 函数/方法签名
• 变量初始化

name: str = "Python3"
name = "Python3" # type checker know it’s a str
def greeting(name: str) -> str
	return 'Hello ' + name

多种类型(Union)

from typing import Union
def accept_task(task_id: int) -> None:
    task_type: Union[str, int]
    if is_side_task(task_id):
        task_type = "Side Task"
    else:
		task_type = 1

可选import(Optional)

from typing import Optional
def accept_task(task_id: int) -> None:
    task_type: Optional[str] #这两种可选写法都ok
    task_type: str | None #这两种可选写法都ok
    if is_side_task(task_id):
        task_type = "Side Task"
    else:
        task_type = None

条件import(TYPE_CHECKING)

原来的import存在以下问题:

from data.config.monster import MonsterConfig
def spawn_monster(monster: MonsterConfig) -> None:
	...

可能需要避免import的情况:
• 会造成循环import
• Import有side-effect或者特定的时机要求
• Import耗时太长

增加条件后的import

from __future__ import annotations
import typing
if typing.TYPE_CHECKING:
	from data.config.monster import MonsterConfig
def spawn_monster(monster: MonsterConfig) -> None:
	...

前向引用(前向声明)

类用字符串来代替?或者导入annotations

NewType

在Python中,类型别名是一个方便的方式,用于为复杂的类型标注提供一个简单的名称。你可以使用 typing.TypeVartyping.NewType 创建类型别名。

例如,如果你有一个复杂的类型,如 List[Tuple[str, str, int]],你可以创建一个类型别名来简化它:

from typing import List, Tuple, TypeVar

PersonInfo = List[Tuple[str, str, int]]

def get_people_info() -> PersonInfo:
    return [('Alice', 'Engineer', 30), ('Bob', 'Doctor', 40)]

在这个例子中,PersonInfo 是一个类型别名,代表 List[Tuple[str, str, int]] 类型。这样可以使代码更易读,更易维护。

Callable

在Python中,typing模块提供了Callable,这是一个类型提示,用于表示可调用的类型,比如函数或方法。

下面是一个使用Callable的例子:

from typing import Callable

def apply_func(x: int, func: Callable[[int], int]) -> int:
    return func(x)

def double(x: int) -> int:
    return 2 * x

print(apply_func(5, double))  # 输出:10

在这个例子中,apply_func函数接受一个整数x和一个函数func作为参数。func的类型被注解为Callable[[int], int],这表示它是一个接受一个整数参数并返回一个整数的函数。double函数就是这样一个函数,所以我们可以将它作为apply_func的参数。

Protocol

由于python函数中的参数无类型,或者说我们不关心对象的类型只关心它能做什么,所以可以通过隐式或某个规则来绕过typing的检查?

在Python 3.8及以上版本中,typing模块提供了Protocol类,它是一种特殊的类,用于定义结构化的类型协议。如果一个类满足了Protocol的定义,那么它就被认为是实现了该协议,无论它是否显式地继承了Protocol

下面是一个使用Protocol的例子:

from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None:
        ...

def close_resource(resource: SupportsClose) -> None:
    resource.close()

class Resource:
    def close(self) -> None:
        print("Resource closed")

resource = Resource()
close_resource(resource)  # 输出:Resource closed

在这个例子中,SupportsClose是一个Protocol,它定义了一个close方法。close_resource函数接受一个SupportsClose类型的参数。尽管Resource类并没有显式地继承SupportsClose,但是它实现了close方法,所以它被认为是实现了SupportsClose协议,可以作为close_resource的参数。

泛型Generic

在Python 3中,typing模块提供了Generic类,用于定义泛型类型。泛型类型是指在定义时不指定具体类型,而在实例化时才确定具体类型的类型。

下面是一个使用Generic的例子:

from typing import Generic, TypeVar

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self.items = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()

# 使用时指定具体类型
stack = Stack[int]()
stack.push(1)
print(stack.pop())  # 输出:1

在这个例子中,Stack是一个泛型类,它接受一个类型参数T。在实例化Stack时,我们需要指定T的具体类型。例如,Stack[int]()创建了一个只接受int类型元素的栈。

使用泛型可以提高代码的复用性,使得我们可以用一套代码来处理多种类型的数据。同时,它也可以提高类型安全性,因为我们可以在编译时检查类型的正确性。

协变与逆变Covariant & Contravariant

变成c++的写法了?这个章节的内容非常多

Mypy Plugin

目前使用最多的pright,由微软主导是vscode自带的

mypy使用的较少

https://mypy.readthedocs.io/en/stable/extending_mypy.html
• 缺少文档
• 和其他type checker不兼容

Overload

如果项目比较特殊,非要使用mypy的话,通过overload来实现

pyi文件

只包含函数和变量类型“声明”的Python文件,Pyi文件的优先级高于py文件
使用场景:
• C/C++ extension模块的类型标注
• Python2/Python3兼容代码的类型标注
• Import耗时较长的模块的类型标注
• 为原本没有类型标注的第三方库添加类型标注
放置位置:
• 位于py文件同级目录
• 位于单独的stub目录(通过参数传递给type checker)

老项目引入 type checking

兼容老代码

迭代老代码

逐步将老的代码文件从exclude中移除
• 更新或为第三方库添加type hint
• 移除disable_error_code
• 单独跳过特定的第三方库

[[tool.mypy.overrides]]
module = "some_legacy_third_party"
follow_imports = skip

禁止未标注的新代码

运行时应用(Runtime API )

类型标注Annotation

import typing
typing.get_type_hints
typing.get_origin
assert get_origin(str) is None
assert get_origin(Dict[str, int]) is dict
assert get_origin(Union[int, str]) is Union
P = ParamSpec('P')
assert get_origin(P.args) is P
assert get_origin(P.kwargs) is P
typing.get_args
assert get_args(int) == ()
assert get_args(Dict[int, str]) == (int, str)
assert get_args(Union[int, str]) == (int, str)

PEP 563

PEP 563’s default change is clearly too disruptive to downstream users
and third-party libraries to happen right now. We can’t risk breaking even
a small subset of the FastAPI/pydantic users, not to mention other uses of
evaluated type annotations that we’re not aware of yet

PEP 563 的默认更改显然对下游用户和第三方库的破坏性太大,目前无法实现。我们不能冒险破坏 FastAPI/pydantic 用户的一小部分,更不用说我们尚不知道的评估类型注释的其他用途

Pydantic

定义每个class中字段的类型,强制指定类型和字段

FastAPI

调用者就相当于在使用强类型语言

Runtime API

Bevy Style ECS API

bevy是一个使用ecs模式的游戏引擎

Mypyc

Cython 3.0

使用cython在运行时热更替换C代码

总结

Type hint能够帮助我们提早发现程序中的类型错误
• 我们可以逐步分阶段在项目中引入type hint
• 我们可以在运行时合理的利用type hint
• Type hint可以帮助我们编译出性能更高的程序
• Python的type hint还在快速的发展中,要用动态的眼光去看代它