全面解析 Python typing模块与静态类型注解:从基础到高级
在现代软件开发中,代码的可读性、维护性和可靠性至关重要。Python 作为一门动态类型语言,尽管灵活,但也可能带来一些类型上的困扰。Python 的 typing
模块和静态类型注解提供了一种在编写代码时明确类型信息的方法,从而提升代码质量。本篇文章将全面解析 typing
模块和静态类型注解,带你从基础到高级,掌握这些强大工具的使用技巧。
深入理解 Python 静态类型注解
在传统的动态类型语言(如 Python)中,变量和函数参数的类型是在运行时而不是编译时确定的。虽然这种动态性使得开发过程更加灵活,但也带来了一些问题,比如无法在编译阶段捕捉类型错误。这就意味着一些类型错误只能在运行时才会被发现,可能会导致应用程序崩溃或产生难以调试的错误。
静态类型注解是一种在代码中显式声明变量和函数参数类型的方法。通过使用类型注解,开发者可以更早地捕捉类型错误,提高代码的可读性和可维护性。
一个简单的例子
让我们从一个简单的例子开始:
def add(a, b):return a + b
在这个函数中,我们没有明确指定 a
和 b
的类型。Python 会假设 a
和 b
可以是任何类型。现在,我们使用静态类型注解来明确指定他们的类型:
def add(a: int, b: int) -> int:return a + b
在这个版本中,a
和 b
必须是整数,且函数的返回类型也是整数。这种明确的声明可以帮助开发者理解和使用函数。
如何使用 Python 的 typing
模块
Python 的 typing
模块提供了一组工具和类型来帮助进行静态类型注解。它包含了许多常见的数据类型,如 List
、Dict
、Tuple
等,还包括一些高级类型和泛型类型。
基础类型注解详解:List、Dict 等
让我们从一些基本类型注解开始:
from typing import List, Dictdef process_items(items: List[str]) -> Dict[str, int]:result = {}for item in items:result[item] = len(item)return result
在这个例子中,我们使用 List[str]
来声明 items
是一个包含字符串的列表,而返回类型是 Dict[str, int]
,表示一个键为字符串、值为整数的字典。
处理可选值和多种类型:使用 Optional
和 Union
有时候,函数参数可以是多种类型或者可以是 None
。这时我们可以使用 Optional
和 Union
:
from typing import Optional, Uniondef greet(name: Optional[str] = None) -> str:if name:return f"Hello, {name}!"else:return "Hello, world!"def process_value(value: Union[int, str]) -> str:if isinstance(value, int):return f"Processed integer: {value}"else:return f"Processed string: {value}"
在这里,我们使用 Optional[str]
表示 name
可以是 str
或 None
。Union[int, str]
则表示 value
可以是 int
或 str
。
高效使用泛型类型与容器
泛型类型允许我们定义一些在类型上更灵活的结构。例如,我们可以定义一个泛型函数来处理任何类型的列表:
from typing import TypeVar, ListT = TypeVar('T')def reverse_list(lst: List[T]) -> List[T]:return lst[::-1]
在这个例子中,TypeVar('T')
定义了一个泛型类型变量 T
。reverse_list
函数接受一个包含任意类型元素的列表,并返回一个相同类型的列表。
自定义泛型类
我们还可以定义自定义的泛型类:
from typing import TypeVar, Generic, ListT = TypeVar('T')class Stack(Generic[T]):def __init__(self):self._items: List[T] = []def push(self, item: T) -> None:self._items.append(item)def pop(self) -> T:return self._items.pop()def is_empty(self) -> bool:return not self._items
在这个例子中,我们创建了一个泛型类 Stack
,它可以容纳任何类型的元素。通过使用 Generic[T]
,我们可以在类中使用泛型类型变量 T
。
类型检查工具:使用 mypy
使用类型注解的一个主要好处是可以借助静态类型检查工具(如 mypy
)来提前捕捉类型错误。mypy
是一个流行的 Python 类型检查器,它可以扫描你的代码并报告任何类型不匹配的问题。
安装和使用 mypy
首先,你需要安装 mypy
:
pip install mypy
然后,你可以使用 mypy
来检查你的代码。例如,假设你有以下代码:
def add(a: int, b: int) -> int:return a + bresult = add(1, "two")
你可以通过运行以下命令来检查类型错误:
mypy your_script.py
mypy
将报告类型错误:
your_script.py:4: error: Argument 2 to "add" has incompatible type "str"; expected "int"
通过使用 mypy
,你可以在编写和维护代码时更早地发现类型问题,从而提高代码的可靠性。
高级类型注解
除了基本的类型注解,typing
模块还提供了一些高级类型注解,适用于更复杂的情况。让我们来看看其中一些。
Callable 类型注解
有时候,你可能需要注解一个函数参数,这个参数本身也是一个函数。Callable
类型可以帮助你做到这一点:
from typing import Callabledef operate(x: int, y: int, func: Callable[[int, int], int]) -> int:return func(x, y)def add(a: int, b: int) -> int:return a + bresult = operate(5, 3, add)
print(result) # 输出:8
这里,Callable[[int, int], int]
表示一个接受两个 int
参数并返回 int
的函数。
Any 和 NoReturn
Any
类型表示可以是任何类型,而 NoReturn
表示一个函数不会返回任何值(通常是因为函数会引发异常或无限循环)。
from typing import Any, NoReturndef handle_data(data: Any) -> None:print(data)def infinite_loop() -> NoReturn:while True:pass
使用 TypedDict
创建类型安全的字典
在某些情况下,你可能需要一个结构化的字典,例如一个包含特定键和类型的配置字典。TypedDict
可以帮助你实现这一点:
from typing import TypedDictclass Config(TypedDict):host: strport: intdebug: boolconfig: Config = {"host": "localhost","port": 8080,"debug": True
}
在这个例子中,Config
是一个 TypedDict
,定义了一个字典的结构,其中 host
是一个字符串,port
是一个整数,debug
是一个布尔值。通过这样定义,你可以确保在使用 config
字典时,键和值的类型是正确的。
使用 NewType
创建区分类型
有时候,不同的值可能具有相同的基本类型,但你希望在类型系统中将它们区分开来。这时可以使用 NewType
:
from typing import NewTypeUserId = NewType('UserId', int)
ProductId = NewType('ProductId', int)user_id = UserId(42)
product_id = ProductId(42)def process_user(user_id: UserId) -> None:print(f"Processing user with ID: {user_id}")# 这样调用是合法的
process_user(user_id)# 这样调用会被类型检查器标记为错误
process_user(product_id)
在这个例子中,我们使用 NewType
创建了两个新的类型 UserId
和 ProductId
,它们都基于 int
类型,但在类型检查时被视为不同的类型。
自定义类型检查:使用 Protocol
和 @runtime_checkable
有时候,内置的类型注解可能无法满足你的需求。这时,你可以使用 Protocol
和 @runtime_checkable
来创建自定义类型检查。
使用 Protocol
定义接口
Protocol
是一种定义接口的方法,可以在类型检查时确保某个类实现了特定的方法:
from typing import Protocolclass Drawable(Protocol):def draw(self) -> None:...class Circle:def draw(self) -> None:print("Drawing a circle")class Square:def draw(self) -> None:print("Drawing a square")def render(shape: Drawable) -> None:shape.draw()circle = Circle()
square = Square()render(circle)
render(square)
在这个例子中,Drawable
是一个协议,定义了一个需要实现的 draw
方法。Circle
和 Square
类都实现了这个方法,因此可以作为 render
函数的参数。
使用 @runtime_checkable
进行运行时检查
默认情况下,Protocol
只在静态类型检查器中生效。如果你需要在运行时检查一个对象是否实现了某个协议,可以使用 @runtime_checkable
装饰器:
from typing import Protocol, runtime_checkable@runtime_checkable
class Drawable(Protocol):def draw(self) -> None:...class Circle:def draw(self) -> None:print("Drawing a circle")def check_if_drawable(obj: object) -> None:if isinstance(obj, Drawable):print("Object is drawable")else:print("Object is not drawable")circle = Circle()
check_if_drawable(circle) # 输出:Object is drawable
通过使用 @runtime_checkable
,你可以在运行时检查某个对象是否符合协议定义。
静态类型注解的局限性与注意事项
虽然静态类型注解和 typing
模块提供了许多便利,但它们也有一些局限性和注意事项。
动态特性与类型检查
Python 是一门动态类型语言,这意味着一些动态特性无法在编译时进行类型检查。例如,动态创建类或函数,使用元类等。这些情况仍然需要依赖运行时检查。
运行时开销
类型注解本身不会对运行时性能产生影响,但如果你使用了一些需要在运行时进行类型检查的工具(如 TypedDict
、Protocol
等),可能会带来一些额外的开销。
类型注解的维护成本
在一个大型的代码库中,使用类型注解可能会增加维护成本。每当你修改函数签名或数据结构时,可能需要更新相应的类型注解。这需要开发者保持代码和类型注解的一致性。
结论
Python 的 typing
模块和静态类型注解为开发者提供了一种在动态语言中使用静态类型检查的强大工具。通过本文的介绍,我们希望你能够更好地理解和应用这些工具,提升代码的质量和可靠性。如果你还没有使用类型注解,不妨在你的下一个项目中试试吧!
此外,欢迎在评论区分享你的使用经验或提出任何问题,我们将共同探讨。
希望这篇文章能帮助你更好地理解和使用 Python 的类型系统。如果你对 Python 编程感兴趣,不妨进一步阅读我们其他关于 Python 的基础语法、如何使用 Python 编写高效代码 的文章。
相关文章:
全面解析 Python typing模块与静态类型注解:从基础到高级
在现代软件开发中,代码的可读性、维护性和可靠性至关重要。Python 作为一门动态类型语言,尽管灵活,但也可能带来一些类型上的困扰。Python 的 typing 模块和静态类型注解提供了一种在编写代码时明确类型信息的方法,从而提升代码质…...
Jekins篇(搭建/安装/配置)
目录 一、环境准备 1. Jenkins安装和持续集成环境配置 2. 服务器列表 3. 安装环境 Jekins 环境 4. JDK 环境 5. Maven环境 6. Git环境 方法一:yum安装 二、JenKins 安装 1. JenKins 访问 2. jenkins 初始化配置 三、Jenkins 配置 1. 镜像配置 四、Mave…...
【工具变量】排污权交易政策试点DID(2000-2023)
数据简介:在过去几十年间的“高增长、高能耗、高污染”的经济发展背景下,随着社会各界不断反应高经济增长背后付出的巨大环境代价,中国ZF将节能环保减排纳入长期规划治理中。在2007年,我国开始启动了二氧化硫(SO2&…...
Proteus中数码管动态扫描显示不全(已解决)
文章目录 前言解决方法后记 前言 我是直接把以前写的 51 数码管程序复制过来的,当时看的郭天祥的视频,先送段选,消隐后送位选,最后来个 1ms 的延时。 代码在 Proteus 中数码管静态是可以的,动态显示出了问题——显示…...
证件照尺寸168宽240高,如何手机自拍更换蓝底
在提供学籍照片及一些社会化考试报名时,会要求我们提供尺寸为168*240像素的电子版证件照,本文将介绍如何使用“报名电子照助手”,借助手机拍照功能完成证件照的拍摄和背景更换,特别是如何将照片尺寸调整为168像素宽和240像素高&am…...
力扣.167 两数之和 II two-sum-ii
数组系列 力扣数据结构之数组-00-概览 力扣.53 最大子数组和 maximum-subarray 力扣.128 最长连续序列 longest-consecutive-sequence 力扣.1 两数之和 N 种解法 two-sum 力扣.167 两数之和 II two-sum-ii 力扣.170 两数之和 III two-sum-iii 力扣.653 两数之和 IV two-…...
ipconfig
本文内容来自智谱清言的回答。 ------ 以太网适配器 以太网: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网: 这部分表示正在显示名为“以太网”的网络适配器的信息。在 Windows 中,默认的以太…...
Qt_day3_信号槽
目录 信号槽 1. 概念 2. 函数原型 3. 连接方式 3.1 自带信号 → 自带槽 3.2 自带信号 → 自定义槽 3.3 自定义信号 4. 信号槽传参 5. 对应关系 5.1 一对多 5.2 多对一 信号槽 1. 概念 之前的程序界面只能看,不能交互,信号槽可以让界面进行人机…...
求从2开始的第n个素数
方法一:暴力法 思路:从2开始,逐个判断每个数是否为素数。素数是除了1和它自身外,不能被其他自然数整除的数。对于每个数m,从2到sqrt(m)遍历,如果能被整除则不是素数。当找到n个素数时停止。 C 代码如下&am…...
【Android】View—基础知识,滑动,弹性滑动
基础知识 什么是View 在 Android 中,View 是用户界面(UI)中的基本组件,用于绘制图形和处理用户交互。所有的 UI 组件(如按钮、文本框、图片等)都是 View 的子类。可以说,View 是构建 Android …...
MYSQL中的两种转义操作
在 MySQL 中,转义字符用于处理特殊字符,以防止语法错误或 SQL 注入攻击,而单双引号都是需要重点注意的字符 可以用转义符\ 和 两个连续的引号 来起到转义引号的作用 转义符转义: 这是users表中的数据 如果查询admin 或者 admin" 用户,可以用转义符\ 两个连…...
力扣题目解析--删除链表的倒数第n个节点
题目 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head [1,2,3,4,5], n 2 输出:[1,2,3,5]示例 2: 输入:head [1], n 1 输出:[]示例 3&…...
Knowledge Graph-Enhanced Large Language Models via Path Selection
研究背景 研究问题:这篇文章要解决的问题是大型语言模型(LLMs)在生成输出时存在的事实不准确性,即所谓的幻觉问题。尽管LLMs在各种实际应用中表现出色,但当遇到超出训练语料库范围的新知识时,它们通常会生…...
Android 项目模型配置管理
Android 项目配置管理 项目模型相关的配置管理config.gradle文件:build.gradle文件: 参考地址 项目模型相关的配置管理 以下是一个完整的build.gradle和config.gradle示例: config.gradle文件: ext {// 模型相关配置࿰…...
「QT」几何数据类 之 QSizeF 浮点型尺寸类
✨博客主页何曾参静谧的博客📌文章专栏「QT」QT5程序设计📚全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…...
Essential Cell Biology--Fifth Edition--Chapter one(2)
1.1.1.3 Living Cells Are Self-Replicating Collections of Catalysts 催化剂集合 生物最常被引用的特性之一是它们的繁殖能力。对于细胞来说,这个过程包括复制它们的遗传物质和其他成分,然后分裂成两个,产生一对子细胞[daughter cells]&a…...
大语言模型LLMs在医学领域的最新进展总结
我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 相比其他学科,医学AI,是发表学术成果最多的领域。 医学数据的多样性和复杂性(包括文本、图像、基因组数据等),使得…...
云防护单节点2T抗攻击能力意味着什么?
随着互联网的发展,DDoS攻击的规模和频率不断增加,对企业和个人用户的网络服务造成了严重威胁。云防护服务作为一种高效的DDoS防护手段,逐渐成为许多企业的首选。本文将重点讨论云防护单节点2T(太比特每秒)抗攻击能力的…...
IDEA在编译时: java: 找不到符号符号: 变量 log
一、问题 IDEA在编译的时候报Error:(30, 17) java: 找不到符号符号: 变量 log Error:(30, 17) java: 找不到符号 符号: 变量 log 位置: 类 com.mokerson.rabbitmq.config.RabbitMqConfig 二、解决方案 背景:下载其他同事代码时,第一次运行,…...
HTML 基础架构:理解网页的骨架
HTML的文档结构主要由以下几个部分组成:<html>、<head>和<body>。 <html>标签是HTML文档的根元素,用来包裹整个HTML文档的内容。<head>标签用于定义文档的头部,包含了一些元数据和其他不直接显示在页面上的内…...
FPGA学习笔记#5 Vitis HLS For循环的优化(1)
本笔记使用的Vitis HLS版本为2022.2,在windows11下运行,仿真part为xcku15p_CIV-ffva1156-2LV-e,主要根据教程:跟Xilinx SAE 学HLS系列视频讲座-高亚军进行学习 从这一篇开始正式进入HLS对C代码的优化笔记 目录 1.循环优化中的基…...
web实操4——servlet体系结构
servlet体系结构 我们基本都只实现service方法,其余几个都不用, 之前我们直接实现servlet接口,所有的方法都必须实现,不用也得写,不然报错,写了又不用当摆设。 能不能只要定义一个service方法就可以&…...
Linux开发讲课48--- Linux 文件系统概览
本文旨在高屋建瓴地来讨论 Linux 文件系统概念,而不是对某种特定的文件系统,比如 EXT4 是如何工作的进行具体的描述。另外,本文也不是一个文件系统命令的教程。 每台通用计算机都需要将各种数据存储在硬盘驱动器(HDD)…...
Node.js 模块详解
模块的概念 Node.js 运行在 V8 JavaScript 引擎上,通过 require() 函数导入相关模块来处理服务器端的各种进程。一个 Node.js 模块可以是一个函数库、类集合或其他可重用的代码,通常存储在一个或多个 .js 文件中。 例如,启动一个 Node.js 服…...
大厂面试真题-说说tomcat的优缺点
Tomcat作为服务器,特别是作为Java Web服务器,具有一系列优点和缺点。以下是对其优缺点的详细分析: 优点 开源免费: Tomcat是一个免费、开源的Web服务器,用户可以在任何环境下自由使用,无需支付任何费用。…...
Linux系统编译boot后发现编译时间与Windows系统不一致的解决方案
现象 如下图,从filezilla软件看虚拟机Linux中编译的uboot.img修改时间与Windows系统时间不同 解决过程 在Linux中查看编译的uboot详细信息,从而得到编译时间。终端输入ls -l后,如下图: 结论 说明在Linux是按照Windows系统时…...
WPS Office手机去广高级版
工具介绍功能特点 WPS Office是使用人数最多的移动办公软件,独有手机阅读模式,字体清晰翻页流畅;完美支持文字,表格,演示,PDF等51种文档格式;新版本具有海量精美模版及高级功能 安装环境 [名称…...
Python爬虫基础-正则表达式!
前言 正则表达式是对字符串的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则的字符串”,此字符串用来表示对字符串的一种“过滤”逻辑。正在在很多开发语言中都存在,而非python独有。对其知识点…...
Python处理PDF组件使用及注意事项
在 Python 中处理 PDF 文件时, 使用的组件及注意事项如下: 1. PyPDF2 / PyPDF4 说明: PyPDF2 和 PyPDF4 都是功能强大的 PDF 操作库,适用于合并、拆分、旋转 PDF 文件,提取 PDF 元数据等。PyPDF4 是 PyPDF2 的一个分…...
langgraph_plan_and_execute
整体入门demo 教程概览 欢迎来到LangGraph教程! 这些笔记本通过构建各种语言代理和应用程序,介绍了如何使用LangGraph。 快速入门(Quick Start) 快速入门部分通过一个全面的入门教程,帮助您从零开始构建一个代理&a…...
二手车网站开发PPT/seo研究协会网
1.在Microsoft OutLook2003中选择“工具”菜单里的“电子邮件账户”选项:2.选择添加新电子邮件账户,点击“下一步”3. 选择服务器类型为POP3,点击“下一步”:4. 填写“用户信息”及“登录信息”5. 填写“服务器…...
做网站测试心得/长沙百度网站推广优化
目前为止最全的CURL中文说明了,学PHP的要好好掌握.有很多的参数.大部份都很有用.真正掌握了它和正则,一定就是个采集高手了. PHP中的CURL函数库(Client URL Library Function) curl_close - 关闭一个curl会话 curl_copy_handle - 拷贝一个curl连接资源…...
wordpress仿微信/直通车关键词怎么优化
大家好,我是叶琛,浙江农林大学计算机专业在读,2017年12月参加考研;2018年3月21日被浙江大学计算机技术专业录取。这篇文章融入了10%的鸡汤和90%的干货,可以帮助你:1)多方面了解考研并强化考研的信心&#x…...
网站上飘窗怎么做/北京seo优化排名推广
分类 Linux Daemon 分为 Stand alone 和 Super daemon 两类 1. Stand alone 为独立守护进程,例如 httpd,vsftpd,他们是常驻内存的进程,优点是响应迅速,缺点是占用内存 2. Super daemon 是由一个超级进程负责管理的守护进程&#…...
怎么搭建网站视频教程/seo免费优化工具
效果:当按下左方向键时输出 “LLL”, 当按下右方向键时输出 “RRR”, 当按下上方向键时输出 “UUU”, 当按下下方向键时输出 “DDD”。 #include <stdio.h> #include <windows.h> //控制界面,获取坐标位置…...