2025-04-19 Python 强类型编程
文章目录
- 1 方法标注
- 1.1 参数与返回值
- 1.2 变参类型
- 1.3 函数类型
- 2 数据类型
- 2.1 内置类型
- 2.2 复杂数据结构
- 2.3 类别选择
- 2.4 泛型
- 3 标注方式
- 3.1 注释标注
- 3.2 文件标注
- 4 特殊情形
- 4.1 前置引用
- 4.2 函数标注扩展
- 4.3 协变与逆变
- 4.4 dataclass
- 5 高级内容
- 5.1 接口
- 5.2 泛型的协变/逆变
- 5.3 字面量类型
- 5.4 静态检查
- 5.5 Final 与 final
- 5.6 关闭静态类型检查
- 工具参考
- 参考链接
环境:
- Pycharm Professional 2025.1
- Python 3.12.9
Python 是一门强类型的动态类型语言,可以动态构造脚本执行、修改函数、对象类型结构、变量类型,但不允许类型不匹配的操作。
Python 也提供了类型标注功能,有了类型标注提示后,就可以在编码时即发现错误。
强类型检查的优势
- 几大优势:
- 易读:比 docstring 更易理解接口协议,也更易使用第三方库(IDE 工具支持)
- 排错:编码、编译期间即可发现错误(IDE 工具支持)
- 重构:接口范围明确,更易于理解和放心重构。
- 性能:(可能)可以做到静态编译优化。
- 现实:
- 生态-IDE:(方类型检查工具比较全面(PyCharm、VSCode、VIM 等)。
- 生态库工具:(方库有官方或大厂支持且很流行。
- 官方 /mypy-9.5 K star、Facebook/.pyre-5 K Star,Google/pytype-3 K star 等。
- 成熟度:静态检查已被广泛验证有效性,尤其大型工程(dropboxi 迁移 400 万行到静态标注)
- 行业:主流编程语言以静态强类型检查为主(C++、Jva、Go 等);其他动态语言的静态类型扩展迅猛发展(如 TypeScript)。
如下是一个非常长的(14个)PEP(Python 改进建议)的列表和落地情况(图中时间是文档时间 +1 年左右是实际落地时间),并且在 PEP 483 开始快速迭代(图中橙色是比较重要的迭代),并且到了 Python3.7 才真正算是成型。
什么时候建议采用强类型检查
-
提供 SDK、库/接口给其他人时。
比 docstring 更清晰、主流 IDE 支持提示和校验。
-
代码行数越多,价值越大。
规范化编码,通过工具可以辅助发现潜在 BUG。
-
需要写 UT(单元测试)的地方,就需要类型检查(by Bernat Gabor)。
以下情况适当考虑
- 原型(Prototype)或验证性质项目(POC)的代码,可以先不引入。
- 大量旧有代码,需要逐步阶段性引入(参考 Dropbox 经验)。
- 不熟悉 Python 和类型提供功能用法时,可以先不引入。
1 方法标注
1.1 参数与返回值
对函数参数,返回值进行标注。
def add(v1: int, v2: int) -> int:"""两个数字相加"""return v1 + v2
1.2 变参类型
对变参、命名变参直接标注其类型。
def foo(*args: str, **kwds: int):...foo('a', 'b', 'c')
foo('a', 'b', 1) # 错误
foo(x=1, y=2)
foo(x=1, y='x') # 错误
foo('', z=0)
1.3 函数类型
函数类型:Callable[[参数1类型, 参数2类型, ...], 返回类型]
2 数据类型
2.1 内置类型
对内置类型标注,在变量声明后面添加 :类型 即可。
a: int = 100
b: str = "abc"
2.2 复杂数据结构
Sequence
Sequence表示server可以是任何序列类型(包括list,tuple,collections.deque等)。Sequence更通用,允许调用方传入列表、元组或其他序列类型。list表示server必须是一个具体的列表(list),不能是元组或其他序列类型。
from collections.abc import SequenceConnectionOptions = dict[str, str]
Address = tuple[str, int]
Server = tuple[Address, ConnectionOptions]def connect(message: str,server: Sequence[Server]) -> None:...# 等价于
def connect1(message: str,server: Sequence[tuple[tuple[str, int], dict[str, str]]]) -> None:...
2.3 类别选择
- 使用
Union定义可能的类型选择:x、y、返回值的类型同一时刻可以不同。 - 使用
TypeVar定义一组类型:x、y、返回值的类型同一时刻必须相同。
from typing import Union, TypeVar, TextAnyStr1 = Union[Text, bytes]
AnyStr2 = TypeVar('AnyStr2', Text, bytes)def concat1(x: AnyStr1, y: AnyStr1) -> AnyStr1:passdef concat2(x: AnyStr2, y: AnyStr2) -> AnyStr2:passconcat1('a', 'b') # 正确
concat1(b'a', b'b') # 正确
concat1('a', b'b') # 正确
concat2('a', 'b') # 正确
concat2(b'a', b'b') # 正确
concat2('a', b'b') # 错误
2.4 泛型
使用 TypeVar 表示泛型。
容器泛型示例 1
T = TypeVar('T') # 一个泛型类型# 接受一个元素类型都是T的序列,返回值的类型也是T
def first(l: Sequence[T]) -> T: # 泛型函数return l[0]first([1, 2]) + 100 # 正确
first(['a', 'c']).upper() # 正确
first(['a', 'c']) + 100 # 错误
容器泛型示例 2
from typing import TypeVar, IterableT = TypeVar('T', bound=float)
Vector = Iterable[tuple[T, T]]def inproduct(v: Vector[T]) -> T:return sum(x * y for x, y in v)
类与泛型
from typing import TypeVar, GenericT = TypeVar('T')class MyClass(Generic[T]):def meth_1(self, x: T) -> T:...def meth_2(self, x: T) -> T:...a: MyClass[int] = MyClass()
a.meth_1(1) # 0K
a.meth_2('a') # 错误
3 标注方式
3.1 注释标注
- 使用注释
# type: xxxx进行标注 - 函数的标注方式:
(参数类型列表)->返回值类型
from typing import Listclass A(object):def __init__(self):# type: ()->Noneself.elements = [] # type: List[int]def add(self, element):# type: (List[int])->Noneself.elements.append(element)
3.2 文件标注
- 独立的 stub 文件(.pyi)与源文件并行即可(放在同一目录下)。
- 优点:
- 不需要修改源代码(减少引入 BUG 可能)
- Pyi 可以使用最新的语法(源文件可以是低版本)
- 测试友好。
- 不拥有的三方库,也可以补充标注信息。
- 缺点:
- 代码重复写了一遍头(工作量)
- 打包变得复杂
# pyi文件from typing import Listclass A(object):elements = ... # type: List[int]def __init__(self) -> None:...def add(self, element: int) -> None:...
4 特殊情形
4.1 前置引用
二叉树节在进行类型标注时需要引用自己,这种情况下需要使用前置引用,方式是用字符串代替类型标注。
from typing import Listclass Tree:def __init__(self, left: 'Tree', right: 'Tree'):self.left = leftself.right = rightdef leaves(self) -> List['Tree']:...
4.2 函数标注扩展
- 使用
overload进行参数返回值描述。 - 必须有一个无修饰版本做真正实现。
- 仅用于静态类型检查,运行时重载可以使用 functools.singleddispatch 等。
from fontTools import unicode
from typing import overload@overload
def utf8(value: None) -> None:...@overload
def utf8(value: bytes) -> bytes:...@overload
def utf8(value: unicode) -> bytes:...def utf8(value):... # 实际的实现,必须有
有时也可以用 TypeVar 代替(更简洁)。
from typing import TypeVarAnyStr = TypeVar('AnyStr', None, unicode, bytes)
AnyStrRet = TypeVar('AnyStrRet', None, unicode, bytes)def utf8(value: AnyStr) -> AnyStrRet:... # 实际实现
4.3 协变与逆变
-
协变:
让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值);
例如:老鹰列表赋值给鸟列表。 -
逆变:
让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确。
例如:鸟列表赋值给老鹰列表。
from typing import TypeVar, GenericT_co = TypeVar('T_co', covariant=True) # 协变class MyList(Generic[T_co]):def __init__(self, items: List[T_co]) -> None:self.items = itemsclass Bird:...class Eagle(Bird):...egls: MyList[Eagle] = MyList([Eagle()])
brds: MyList[Bird] = egls
4.4 dataclass
- 带默认值的可变命名元组
from dataclasses import dataclass, field@dataclass
class InventoryItem:name: strunit_price: floatquantity_on_hand: int = 0 # 默认值
- 复杂域默认值
@dataclass
class C:mylist: List[int] = field(default_factory=list)c = C()
c.mylist += [1, 2, 3]
- 延迟初始化
@dataclass
class C:a: floatb: floatc: float = field(init=False) # 默认不初始化 cdef __post_init__(self): # 通过延迟初始化 cself.c = self.a + self.bprint(C(1, 2)) # print: "C(a=1, b=2, c=3)"
5 高级内容
5.1 接口
Python 支持 OOP,也支持 duck typing。在强类型检查中,duck typing 也一样适用。
例如一个函数 close_resource 接受一个参数,要求这个参数必须提供一个特定的方法 .close()。这种需求在强类型检查时,也可以支持,称之为静态 duck typing,相关方案由 PEP 544 支持。
- 定义了类似于 Java 的接口
- 可以有实现,可以被继承。
- 可以继承多个接口,构建一个新接口。
- 实际类型检查时,不要求继承关系
- 只需要对象的所有成员方法 signature 匹配 Protocoll 即可(duck-typing)。
from typing import Protocol, Iterableclass IResource(Protocol):def close(self) -> None:...class Resource: # 也可以继承IResource(非必须)def close(self) -> None:...def close_all(things: Iterable[IResource]) -> None:for t in things:t.close()f = open('foo.txt')
r = Resource()
close_all([f, r]) # 通过
close_all([1]) # 不通过
5.2 泛型的协变/逆变
-
协议 (
Protocol) 必须被实现Box是抽象接口,不能直接实例化,必须有一个类实现content()方法。
-
协变 (
covariant=True) 的规则-
Box[int]可以赋值给Box[float],因为int是float的子类型(协变允许子类替换父类)。 -
协变表示:如果
A是B的子类型,则Box[A]是Box[B]的子类型。 -
int是float的子类型(因为所有int都可安全当作float使用),所以Box[int]可赋值给Box[float]。
-
from typing import TypeVarT_co = TypeVar('T_co', covariant=True) # 支持协变或逆变class Box(Protocol[T_co]):def content(self) -> T_co:...# 具体实现
class IntBox:def content(self) -> int:return 42class FloatBox:def content(self) -> float:return 3.14# 正确赋值
second_box: Box[int] = IntBox() # ✅ 实现 Box[int]
box: Box[float] = second_box # ✅ 协变允许(int 是 float 的子类型)
5.3 字面量类型
不修改类型为 enum 的情况下,限定传递参数:Literal[字面量1,字面量2,]。
from typing import Literal, Any# 总是返回True
def validate_simple(data: Any) -> Literal[True]:...# 只能是这几个值
MODE = Literal['r', 'rb', 'w', 'wb']def open_helper(file: str, mode: MODE) -> str:...open_helper('/some/path', 'r') # 通过
open_helper('/other/path', 'typo') # 不通过
5.4 静态检查
- 可以在静态检查时导入特定的库,运行时不做。
- 这种情况下,相关标注只能用注释或字符串(前置)方式标注。
import typingif typing.TYPE_CHECKING:import expensive_moddef a_func(arg: 'expensive_mod.Someclass') -> None:a_var = arg # type: expensive_mod.Someclass
5.5 Final 与 final
Final
在 Python 3.8 版本中引入一个扩展的标注 typeing.Final,用于标注变量,这个内置标注非常有用,可以理解为实现了 C++ 语法 const 的静态检查作用。
- 指定变量被初始化后无法再被修改、类变量无法被子类修改。
- 声明为 Final 的类成员的变量,未初始化的,必须在
__init__里面初始化。
from typing import FinalMAX_SIZE: Final = 9000
MAX_SIZE += 1 # 错误class ImmutablePoint:x: Final[int]y: Final[int] # 错误def __init__(self) -> None:self.x = 1 # 未初始化ydef s(self):self.x = 1 # x不能被重新赋值
final
另一个被引入的就是小写的 typing.final,用于标注类,可以理解为实现了 Java 语法 final 的静态检查作用。虽然 Python 本身就可以扩展实现运行时 final 的作用,但是这里实现了检查期的final,这个官方版本可以说非常的有用。
如下,除了可以修饰类(不能被继承),甚至可以修饰类的方法(不能被重写)
from typing import final@final
class Base:...class Derived(Base): # 错误...class Base2:@finaldef foo(self):...class Derived2(Base2):def foo(self): # 错误...
5.6 关闭静态类型检查
特定情况下,我们也需要关闭静态检查(例如测试或开发中间时),这种情况只需要使用 typing.no_type_check 修饰函数或类来关闭。
如果希望关闭对一个装饰器的静态检查的话,需要使用 typing.no_type_check_decorator 修饰装饰器来关闭。
from typing import no_type_check, no_type_check_decorator@no_type_check
def add(v1: int, v2: int) -> int:...@no_type_check_decorator
def log_enter_exit(fn):def __wrapped(*args: int, **kwargs: int):...return __wrapped
工具参考
- typeshed:Python 内置标准和三方库的 pyi 集合 repo(PyCharm、mypy、pytype 已包含)
- mypy:官方标准静态类型检查工具。
- pyre:Facebook 开源的静态类型检查工具。
- pytype:google 开源的 Python 静态代码扫描工具(不依赖标注)。
- pyannotate:dropbox 开源的自动给 Python 添加类型标注的工具。
- pydantic:一个基于标注的 Python 数据校验与配置管理库。
参考链接
- 丁来强-Python强类型编程最佳实践_哔哩哔哩_bilibili。
- Python的强类型特性与类型检查实践-CSDN博客。
相关文章:
2025-04-19 Python 强类型编程
文章目录 1 方法标注1.1 参数与返回值1.2 变参类型1.3 函数类型 2 数据类型2.1 内置类型2.2 复杂数据结构2.3 类别选择2.4 泛型 3 标注方式3.1 注释标注3.2 文件标注 4 特殊情形4.1 前置引用4.2 函数标注扩展4.3 协变与逆变4.4 dataclass 5 高级内容5.1 接口5.2 泛型的协变/逆变…...
Java大模型MCP服务端开发-数据库查询与数据分析(附源码)
Java大模型MCP服务端开发-数据库查询 MCP服务器概述安装依赖服务端对象服务器传输服务器功能客户端测试源码地址MCP服务器概述 MCP服务器是模型上下文协议(MCP)架构中的基础组件,为客户端提供工具、资源和功能。它实现了协议的服务器端,负责: 暴露客户端可以发现和执行的工…...
spring-batch批处理框架(2)
文章目录 八、作业控制8.1 作业启动8.1.1 SpringBoot 启动8.1.2 Spring 单元测试启动8.1.3 RESTful API 启动 8.2 作业停止方案1:Step 步骤监听器方式方案2:StepExecution停止标记 8.3 作业重启8.3.1 禁止重启8.3.2 限制重启次数8.3.3 无限重启 九、Item…...
鸿蒙NEXT开发键盘工具类(ArkTs)
export declare type KeyboardCallBack (show: boolean, height: number) > void; import { AppUtil } from ./AppUtil; import { LogUtil } from ./LogUtil; import { ArrayUtil } from ./ArrayUtil;/*** 键盘工具类* author 鸿蒙布道师* since 2025/04/18*/ export class…...
动态规划算法的欢乐密码(一):斐波那契数模型
专栏:算法的魔法世界 个人主页:手握风云 目录 一、动态规划 二、例题讲解 2.1. 第 N 个泰波那契数 2.2. 三步问题 2.3. 使用最小花费爬楼梯 2.4. 解码方法 一、动态规划 动态规划是一种将复杂问题分解为更小的子问题,并利用子问题的解来…...
Echarts柱状图斜线环纹(图形的贴花图案)
单独设置 <!--此示例下载自 https://echarts.apache.org/examples/zh/editor.html?cbar-stack&codePYBwLglsB2AEC8sDeAoWszGAG0iAXMmuhgE4QDmFApqYQOQCGAHhAM70A0x6L7ACsAjQwtQqhIkwATxDUGbABaMAJsADu9HrAC-xHd3TZqNaCvEHiFcuaKTjAMzAMAzAFIu28hUXPY9ABYPQxIAI2AwTABbV…...
2025.04.19【Spider】| 蜘蛛图绘制技巧精解
Basic multi-group radar chart Start with a basic version, learn how to format your input dataset Radar chart with ggradar A Spider chart made using the ggradar package and a lot of customization.A work by Tuo Wang 文章目录 Basic multi-group radar chartRa…...
机器学习误差图绘
机器学习误差图绘制 绘图类 # Define the ModelComparisonPlot class class ModelComparisonPlot:def __init__(self, model_name):self.model_name model_namedef plot_comparison(self, y_val, y_pred, mse, mae, r2):# Create a figure with two subplotsfig, axes plt.…...
llama-factory微调报错:
报错信息 [INFO] [utils.py:789:see_memory_usage] CPU Virtual Memory: used 81.51 GB, percent 64.9% W0419 10:14:27.573000 108354 site-packages/torch/distributed/elastic/multiprocessing/api.py:897] Sending process 108373 closing signal SIGTERM W0419 10:14:27…...
【Linux】深入理解Linux文件系统:从C接口到内核设计哲学
文章目录 前言一、C语言中的文件接口1. 文件指针(句柄)FILE*以写方式打开文件,若文件不存在会新建一个文件W写入方式,在打开文件之前都会将文件内容全部清空追加写方式,其用法与写方法一致,不同在于a方法可…...
基于尚硅谷FreeRTOS视频笔记——15—系统配制文件说明与数据规范
目录 配置函数 INCLUDE函数 config函数 数据类型 命名规范 函数与宏 配置函数 官网上可以查找 最核心的就是 config和INCLUDE INCLUDE函数 这些就是裁剪的函数 它们使用一个ifndef。如果定义了,就如果定义了这个宏定义,那么代码就生效。 通过ifn…...
Linux网络编程 深入解析TFTP协议:基于UDP的文件传输实战
知识点1【TFTP的概述】 学习通信的基本:通信协议(具体发送上面样的报文)、通信流程(按照什么步骤发送) 1、TFTP的概述 tftp:简单文件传输协议,**基于UDP,**不进行用户有效性验证 …...
c# MES生产进度看板,报警看板 热流道行业可用实时看生产进度
MES生产进度看板,报警看板 热流道行业可用实时看生产进度 背景 本软件是给宁波热流道行业客户开发的生产电子看板软件系统 功能 1.录入工艺流程图(途程图)由多个站别组成。可以手动设置每个工艺站点完成百分比。 2.可以看生成到哪个工…...
Qt unknown module(s) in qt:serialport解决方法
在Ubuntu和CentOS系统中,若使用Qt时遇到Unknown module(s) in QT: serialport错误,通常是由于未正确安装Qt的串口模块(QSerialPort)或项目配置不当导致。以下是针对两种系统的解决方案: 一、安装Qt串口模块 1. Ubuntu/Debian系列 安装开发包: 执行以下命令安装Qt5串口模…...
AtCoder ABC402 A~D 题解
A - CBC 题目大意 给点字符串 S S S,输出其中所有大写字母。 思路 根据题意模拟即可。 代码 #include <cstdio> #include <iostream> #include <algorithm> using namespace std;int main() {string s;cin >> s;for (int i 0; i &l…...
2025.04.19-阿里淘天春招算法岗笔试-第二题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 秒杀顺子查找 问题描述 K小姐是一名热爱扑克牌的玩家。她定义一个数列是"顺子",当且仅当将该数列排序后,每个元素恰好比前一个元素大 1 1...
初识Redis · C++客户端string
目录 前言: string的API使用 set get: expire: NX XX: mset,mget: getrange setrange: incr decr 前言: 在前文,我们已经学习了Redis的定制化客户端怎么来的,以及如何配置好Redis定制化客户端&…...
华硕原厂系统枪神9/9p超竟版-WIN11原装开箱出厂系统安装
华硕原厂系统枪神9/9p超竟版-WIN11-24H2-专业工作站版本安装可带F12-ASUSRecovery恢复功能 适用机型: G635LX、G635LW、G835LX、G835LW、G615LW、G615LP、G615LM、G615LH G815LW、G815LP、G815LM、G815LH、G635LR、G835LR、G615LR、G815LR 远程恢复安装ÿ…...
CF1016赛后总结
文章目录 前言T1:Ideal GeneratorT2:Expensive NumberT3:Simple RepetitionT4:Skibidi TableT5:Min Max MEXT6:Hackers and Neural NetworksT7:Shorten the Array 前言 由于最近在半期考试,更新稍微晚了一点,还望大家见谅 &#…...
QT聊天项目DAY06
1.从git上同步项目 编译测试,编译通过 Post请求测试 测试成功 2. email is 打印有问题,检查 解析结果是存储在jsonResult中的,修改 3. 客户端实现Post验证码请求 3.1 同步Qt客户端项目 检查QT版本,由于我在公司用的还是QT5.12.9…...
GNU,GDB,GCC,G++是什么?与其他编译器又有什么关系?
文章目录 前言1. GNU和他的工具1.1 gcc与g1.2 gdb 2.Windows的Mingw/MSVC3.LLVM的clang/clang4.Make/CMake 前言 在开始之前我们先放一段Hello World:hello.c #include <stdio.h>int main() {printf("Hello World");return 0; }然后就是一段老生常…...
【AI提示词】IT专家顾问
提示说明 IT 专家顾问是一位专注于IT行业的专家,拥有深厚的技术背景和广泛的知识储备。他们能够为企业、政府机构或其他组织提供技术支持、解决方案设计和战略规划。 提示词 # Role: IT 专家顾问## Profile - **语言**: 中文 - **描述**: IT 专家顾问是一位专注于…...
笔记整理五
STP生成树 stp生成树是用于解决二层环路问题的协议。 二层环路为有以下三种: 1.广播风暴 2.MAC地址的偏移(每一次循环,都会导致交换机来回刷新MAC地址表记录) 3.多帧复制 stp生成树:需要将原本的环型拓扑结构转换…...
Java中“this”关键字梳理详解
在Java中,this 是一个非常重要的关键字,它表示当前对象的引用。也就是说,当你在某个类的实例方法或构造器中时,this 指向调用该方法或创建的当前对象实例。以下将结合代码示例和具体场景,详细讲解 this 的用法及其作用…...
mybatis plus打印sql日志到指定目录
1、mybatis plus打印sql日志 参考文档:mybatis plus打印sql日志_mybatisplus日志打印-CSDN博客 2、修改 修改InfoLevelLogger Override public void debug(String s) {// 修改这里logger.info(s);log.debug(s); } 增加:log.debug(s); 修改logback.x…...
奥比中光tof相机开发学习笔记
针对奥比中光 tof相机,官方提供的资料如下ProcessOn Mindmap|思维导图 Orbbec SDK Python Wrapper基于Orbbec SDK进行设计封装,主要实现数据流接收,设备指令控制。下面就其开发适配进行如下总结: (1)系统配…...
Oracle游标和触发器
--1.游标 --什么是游标 --游标是数据库在内存中开辟的数据缓冲区 --作用:用于遍历查询返回之后的结果集(多条数据结果) --游标分类:隐式游标,显示游标,REF游标(动态游标) --游标的状…...
【面试向】点积与注意力机制,逐步编码理解自注意力机制
点积(dot product)两个向量点积的数学公式点积(dot product)与 Attention 注意力机制(Attention)注意力机制的核心思想注意力机制中的缩放点积自注意力机制中,谁注意谁? 逐步编码理解…...
00.IDEA 插件推荐清单(2025)
IDEA 插件推荐清单 精选高效开发必备插件,提升 Java 开发体验与效率。 参考来源:十六款好用的 IDEA 插件,强烈推荐!!!不容错过 代码开发助手类 插件名称功能简介推荐指数CodeGeeX智能代码补全、代码生成、…...
一个 CTO 的深度思考
今天和一些同事聊了一会,以下是我的观点 我的观点,成年人只能筛选,不能培养在组织中,应该永远向有结果的人看齐。不能当他站出来讲话的时候,大家还要讨论讨论,他虽然拿到结果了,但是他就是有一…...
