Python进阶特性(类型标注)

news2025/2/27 8:15:40

1.4 Python进阶特性(类型标注)

1.4.1 类型标注介绍

Python属于动态类型语言,只有在运行代码的时候才能够知道变量类型,那么这样容易产生传入参数类型不一致的问题,导致出现意想不到的bug。这也是动态类型语言天生的一个问题。
所以在Python3.5的时候开始引入了类型标注Type Hint),让我们能够显式地标注类型。经过后续版本更新,现在Python中的类型标注功能已经慢慢完善起来。
注意:在Python中添加类型标注静静是在语法层面,对代码的运行没有影响,Python解释器在执行代码的时候会忽略类型提示。

1.4.2 类型标注的优点

提升代码的可读性:可以很方便的得知变量的类型
提升代码的可用性:方便调用者传入、传出正确的类型参数,便于代码重构。
易于检测代码的逻辑问题:此处可以使用mypy工具进行静态检测代码逻辑问题。
提升开发效率:在PyCharm等IDE中,会根据变量的类型标注提示该变量的方法、代码补全等,方便开发。

1.4.3 类型标注语法

1.4.3.1 标注为单类型

最简答的类型标注其实就是为一个变量标注为某个数据类型,如下:
>>> a: str = ‘nanchang’
上述代码就是通过类型标注将变量a标注成了str类型,今后在使用a时,Python会默认a的类型就是str类型,如果遇到类型不符的操作时,就会给出提示。

类型标注在函数中更加常用,通常是用来为函数的参数添加类型标注,这样,当别人传入参数时,如果传入的实参类型与函数形参的类型不一致,则会进行提示,让开发者尽快发现问题。如下:

from icecream import ic

a: str = 'nanchang'


def myPow(n1: int, n2=2):
    ic(n1 ** n2)


myPow(a)

此时,不用运行代码,我的PyCharm就提示类型 'str' 没有预期的特性 '__pow__',并且在最后的代码myPow(a)的a的下方添加了波浪线提示。
为变量添加类型参数还有一个,尤其是函数中的形参,那就是之前提到过的,在IDE中,会根据变量的类型标注提示该变量的方法、代码补全等,方便开发。
在没有添加类型标注之前,对形参使用.符号调用方法时,由于IDE不知道该形参是什么类型,会将Python自带的数据类型的所有方法一股脑全给列出来,不利于找到想要的方法。如下:
在这里插入图片描述

但是,在添加类型标注后,就只会列出指定数据类型的方法,十分方便。如下:
在这里插入图片描述

不关函数的形参可以添加类型标注,函数的返回也可以添加。语法如下:

def myPow(n1, n2=2) -> int:
    ic(n1 ** n2)
    return n1 ** n2

为函数返回值添加类型标注后,函数的返回值如果不是指定的类型,那么Python也会进行提示。

为变量指定其它Python自带单类型的标注如下:

a: bool = True
b: int = 2
c: float = 1.4
d: list = ["A", "B"]
e: dict = {"x": "y"}
f: tuple = ("age", "job")
g: set = {"a", "b"}

1.4.3.2 List

上面介绍了将变量标注为单类型,下面介绍一些其他类型。
如果想将某个变量的类型标注为list,但是内部的元素必须是特定的类型,则可以使用List。

from typing import List

a: List[str] = ['age']


def myfun(var: List[int]):
    return sum(var)

1.4.3.3 Dict

同理,指定字典键值的数据类型,可以使用Dict。

from typing import Dict

my_dict: Dict[str, str] = {"name": "zhangsan", "gender": "man"}

1.4.3.4 Union

如果,要为某个变量指定多个类型,那么可以使用Union。

from icecream import ic
from typing import Union


def myfun(a: Union[str, int]):
    ic(a * 2)


myfun(123)
myfun('123')

15:31:41|> a * 2: 246
15:31:41|> a * 2: ‘123123’

如上,对于myfun函数,如果我们想让它接收int或者str类型,如果是int类型,则返回参数的2倍,如果是str类型,则重复2遍再返回。那么用Union就可以实现了。
PS:从 Python 3.10 开始,Union 被替换为 | 这意味着 Union[X, Y, …] 现在等价于(X | Y | …)。当然,(X, Y, …)也是可以的。

1.4.3.5 Any

当变量的类型可以是任何类型时,可以使用Any。

from icecream import ic
from typing import Any


def myfun(arg: Any):
    ic(arg)


myfun(2)

15:46:41|> arg: 2

1.4.3.6 Sequence

Sequence 类型的对象是可以被索引的任何东西:列表、元组、字符串等。

from icecream import ic
from typing import Sequence


def myfun(arg: Sequence):
    for _ in arg:
        ic(_)


myfun('wasd')

15:51:48|> _: ‘w’
15:51:48|> _: ‘a’
15:51:48|> _: ‘s’
15:51:48|> _: ‘d’

1.4.3.7 Tuple

Tuple 类型的工作方式与 List 类型略有不同,Tuple 需要指定每一个位置的类型,并且数量要一致。

from typing import Tuple

a: Tuple[int, int, int] = (1, 2, 3)  # √
b: Tuple[int, int, str] = (1, 2, 3)  # ×:第3个元素类型不对
c: Tuple[int, int] = (1, 2, 3)  # ×:多了一个元素

1.4.3.8 Optional

Optional意思是说这个参数可以为空或已经声明的类型,即 Optional[类型1] 等价于 Union[类型1, None]。
需要注意的是这个并不等价于可选参数,当它作为参数类型注解的时候,不代表这个参数可以不传递了,而是说这个参数可以传为 None。

from icecream import ic
from typing import Optional


def myfun(arg: Optional[int] = 23):
ic(arg)


myfun()
myfun(None)

15:50:08|> arg: 23
16:50:35|> arg: None

1.4.3.9 Callable

当遇到类型标注为函数时,则可以使用Callable。如下:

from icecream import ic
from typing import Callable


def myfun(fun: Callable, *arg):
ic(fun(*arg))


myfun(max, 1, 2)

15:42:20|> fun(*arg): 2

我们甚至还可以给传入的函数参数指定参数列表及类型,语法如下:
>>> Callable[[input_type_1, …], return_type]

1.4.3.10 NewType

可以使用 NewType() 辅助函数创建不同的类型,有时候我们需要创建一种特定的数据类型,比如:用户id的数据类型。实际上,数据id数据类型就是int类型,但是,在使用时,如果有专门的用户id数据类型的话,会比较方便,并且也易于理解。

from icecream import ic
from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)
ic(type(some_id), some_id)

15:20:35|> t1.py:6 in
type(some_id): <class ‘int’>
some_id: 524313

上面的代码中,创建了一种新的数据类型UserId,其实就是int类型。静态类型检查器会将新类型视为它是原始类型的子类。这对于帮助捕捉逻辑错误非常有用:

from icecream import ic
from typing import NewType

UserId = NewType('UserId', int)


def get_user_name(user_id: UserId):
    ic(user_id)


get_user_name(UserId(42351))
get_user_name(-1)  # 这里会提示,类型不对

11:30:04|> user_id: 42351
11:30:04|> user_id: -1

您仍然可以对 UserId 类型的变量执行所有的 int 支持的操作,但结果将始终为 int 类型。这可以让你在需要 int 的地方传入 UserId,但会阻止你以无效的方式无意中创建 UserId:

from icecream import ic
from typing import NewType

UserId = NewType('UserId', int)

a = UserId(1) + UserId(2)

ic(type(a), a)

15:29:49|> t1.py:8 in - type(a): <class ‘int’>, a: 3

注意,这些检查只由静态类型检查器强制执行。 在运行时,语句 Derived = NewType(‘Derived’, Base) 将产生一个 Derived 函数,该函数立即返回你传递给它的任何参数。 这意味着表达式 Derived(some_value) 不会创建一个新的类,也不会引入超出常规函数调用的很多开销。
更确切地说,表达式 some_value is Derived(some_value) 在运行时总是为真。
这也意味着无法创建 Derived 的子类型,因为它是运行时的标识函数,而不是实际的类型:

from typing import NewType

UserId = NewType('UserId', int)


class AdminUserId(UserId): 
    pass

Traceback (most recent call last):
File “E:/t1.py”, line 7, in
class AdminUserId(UserId):
TypeError: function() argument ‘code’ must be code, not str

然而,我们可以在 “派生的” NewType 的基础上创建一个 NewType。

from typing import NewType

UserId = NewType('UserId', int)

ProUserId = NewType('ProUserId', UserId)

并且 ProUserId 的类型检查将按预期工作。

1.4.4 类型别名

类型别名通过将类型分配给别名来定义。在这个例子中, Vector 和 List[float] 将被视为可互换的同义词:

from typing import List


Vector = List[float]


def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]


new_vector = scale(2.0, [1.0, -4.2, 5.4])

类型别名可用于简化复杂类型签名。例如:

from typing import Dict, Tuple, Sequence


ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]


def broadcast_message(message: str, servers: Sequence[Server]) -> None:
    pass


# 而非以下方式:


def broadcast_message(
        message: str,
        servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    pass

请注意,None 作为类型提示是一种特殊情况,并且由 type(None) 取代。

1.4.5 泛型(TypeVar)

有时候,我们在定义函数时,某些参数或者参数和返回值需要相同的类型,但是,参数或返回值的类型又不固定,可能是int或者str等,这个时候就可以用到泛型了。
简单理解就是泛型其实就是不确定类型标注是标注的类型是什么。类似值不确定,用变量来表示。

from typing import List, TypeVar

T = TypeVar('T')

def myfun(var: T) -> T:
return str(var)

myfun(1)

>>> mypy t1.py

t1.py:7: error: Incompatible return value type (got “str”, expected “T”)
Found 1 error in 1 file (checked 1 source file)

如上,我们创建了一个泛型:T,代表任何类型的数据。在后续定义的myfun中,参数var的类型为T(也就是任何类型都可以),而后函数的返回类型也是T,但此时,返回的类型T的实际类型必须和var一致。
在调用myfun函数时,我们传入的参数的类型是int,但是返回的类型是str,这两者不一致,随后我们在使用第三方模块mypy进行代码检查时就报错了:在第7行的类型和函数传入的参数的类型不一致。
注意:
1、在创建泛型时,如果可以是任何类型,则写法固定:T = TypeVar(‘T’)
2、如果,创建泛型时,是指定的类型,则写法为:A = Typevar(‘T’, int, str…)

from typing import TypeVar

A = TypeVar('A', str, bytes)

def longest(x: A, y: A) -> A:
return x if len(x) >= len(y) else y

ic(longest('abc', 'asdfsdfa'))

18:26:26|> longest(‘abc’, ‘asdfsdfa’): ‘asdfsdfa’

>>> mypy t1.py

Success: no issues found in 1 source file

1.4.6 对可变参数进行标注

在python中,函数的可变类型参数是指可以接受任意数量的值的参数,例如*args和**kwargs。要对这些参数进行类型标注,可以使用typing模块中的特殊类型,例如Any、Tuple、Dict等。也可以使用Python中默认的单类型。

1.4.6.1 标注*args

*args接收后的参数会全部丢到元组中,如果确定*args接收的参数都是同一种类型的,可以按照如下方式标注:

def add(*args: int) ->int:
    sum_value = sum(args)
    return sum_value

def foo(*args: Any) -> None:
    # do something with args
    pass

如果接收到的参数不止一种类型,那么就可以使用Union

from typing import Optional, Union


def add(*args: Union[str, int, float]) -> float:
    sum_value = sum([float(item) for item in args])
    return sum_value

print(add(2, '1', 4.8))

也可以使用Tuple

def baz(*args: Tuple[int, str]) -> int:
    # do something with args
    return 0

传入的可变参数可以是str,int,float中的任意一个,args虽然是元组,但是我们不是按照元组来进行标注,标注的是对这些参数的期望值。

1.4.6.2 标注**kwargs

对于**kwargs,因为会被作为字典接收,所以可以使用Union或Dict。

from typing import Any, Union


def add(**kwargs: Union[int, str, float]) -> None:
print(kwargs)


def baz(**kwargs: Dict[str, Any]) -> int:
# do something with kwargs
return 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/426915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring】— Spring中Bean的装配方式

Spring中Bean的装配方式Bean的装配方式1.基于XML的装配2.基于Annotation的装配3.自动装配Bean的装配方式 Bean的装配可以理解为依赖关系注入&#xff0c;Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean装配方式&#xff0c;如基于XML的装配、基于Annotatio…

电力系统中针对状态估计的虚假数据注入攻击建模与对策(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

免费部署属于自己的chatGPT网站,欢迎大家试玩

最近我发现了一个非常nice的部署网站的工具&#xff0c; railway&#xff0c;这个网站是国外的&#xff0c;所以部署出来的项目域名是国外的&#xff0c;并不需要担心封号&#xff0c;也不需要进行域名注册&#xff0c;部署成功之后会自动生成域名&#xff0c;在国内就能够正常…

[NSSRound#11] 密码学个人赛

这个比赛没有参加,跟别人要了些数据跑一下,其实交互这东西基本上一样,跑通就行. ez_enc 这题有点骗人,给了一堆AB串,一开始以为是培根密码,结果出来很乱.再看长度:192 应该就是01替换 a ABAABBBAABABAABBABABAABBABAAAABBABABABAAABAAABBAABBBBABBABBABBABABABAABBAABBABAA…

“心机boy”马斯克:明面上呼吁暂停先进AI研发,背地里悄悄买1万块GPU推进大模型项目

来源: AI前线 微信号&#xff1a;ai-front 整理 | 冬梅、核子可乐 为了研发自家 AIGC&#xff0c; 马斯克狂买 GPU 并四处挖人 当地时间 4 月 11 日&#xff0c;据多家外媒报道&#xff0c;尽管高调建议在整个行业范围内停止 AI 训练&#xff0c;但伊隆马斯克本人倒是在 T…

VMware:安装centos7

环境&#xff1a; 准备好VMware软件 准备好centos镜像 如有需要 寻找镜像&&真机安装&&真机安装时候找不到硬盘 可以查看 &#xff1a; linux&#xff1a;真机安装centos linux&#xff08;突发事件&#xff1a;解决卡在安装界面&#xff09;{寻找镜像--u…

springboot+vue简历系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的简历系统简历系统。项目源码请联系风歌&#xff0c;文末附上联系信息 。 目前有各类成品java毕设&#xff0c;需要请看文末联系方式 …

Excel中的表格批量生成word表格

场景&#xff1a;测试用例excel转word 我们在项目中&#xff0c;默认情况下是用我们的excel用例模版输出测试用例。但是有的项目中&#xff0c;会要求在word版本的测试计划或者测试报告中&#xff0c;写明测试用例。而我们的测试用例&#xff0c;有的项目有上千条&#xff0c;…

基于遥感的自然生态环境检测——实验一:SPOT全色影像正射纠正

实验流程 实验总流程如下图所示&#xff0c;主要包括四大步&#xff1a; &#xff08;1&#xff09;SPOT全色影像正射纠正&#xff1b; &#xff08;2&#xff09;多光谱与全色影像的配准和融合&#xff1b; &#xff08;3&#xff09;大气校正和生态因子提取&#xff1b; &am…

MathType+MicrosoftWord

MathTypeMicrosoftWordMathType的安装Microsoft WordMathType的安装 链接&#xff1a;https://pan.baidu.com/s/1bEd41GF-yRCRw2tb1XH8og?pwd1234 提取码&#xff1a;1234 语言选择英文&#xff01;&#xff01;&#xff01; 安装好之后&#xff0c;按win键&#xff0c;可以…

Springboot项目实战:一个依赖解决多平台OSS文件上传问题,以后就用这个 oss-spring-boot-starter

本文解决痛点。是否再不同项目需要不同的OSS二头疼。 A项目用七牛云&#xff0c;B项目使用阿里云。不想用七牛云了&#xff0c;还是改用华为云吧。同个项目使用不同的 bucketName 遇到这种种情况&#xff0c;本文提供一个依赖搞定多云OSS 适配问题 什么是OSS? 数据以对象&…

【UE 控件蓝图】菜单及功能实现

素材资源连接&#xff1a;百度网盘 请输入提取码 密码&#xff1a;fvcw 效果 步骤 1. 创建蓝图&#xff0c;父类为“HUD” 命名为“MainMenuHUD”并打开 在事件图表中添加如下节点&#xff1a; 2. 创建控件蓝图&#xff0c;命名为“MainMenuWidget” 此时在“MainMenuHUD”的…

全方位解析 pinia

前言 Vue3已经推出很长时间了&#xff0c;它周边的生态也是越来越完善了。之前我们使用Vue2的时候&#xff0c;Vuex可以说是必备的&#xff0c;它作为一个状态管理工具&#xff0c;给我们带来了极大的方便。Vue3推出后&#xff0c;虽然相对于Vue2很多东西都变了&#xff0c;但…

私有句柄表

私有句柄表 实验环境 win7 x86 什么是私有句柄表&#xff1f; 私有句柄表是操作系统内部的一种数据结构&#xff0c;用于存储一个进程所拥有的句柄&#xff08;或称为句柄对象&#xff09;的信息。在操作系统中&#xff0c;句柄是一个标识符&#xff0c;用于唯一标识一个对…

【iOS】NSError**和__autoreleasing场景

前言 在看JSONModel源码的时候&#xff0c;JSONModel的自定义Error的方法一直在报错 - (BOOL)validate:(NSError *__autoreleasing *)error {}这个方法在定义error的时候添加上了__autoreleasing修饰符&#xff0c;涉及到了__autoleasing的显式隐式调用就去了解了一下。 发现…

【C++ 三】一维数组、二维数组

数组概述、一维数组、二维数组 文章目录数组概述、一维数组、二维数组前言1 数组1.1 概述2 一维数组2.1 一维数组定义方式2.2 一维数组数组名2.3 冒泡排序3 二维数组3.1 二维数组定义方式3.2 二维数组数组名总结前言 本文包含数组概述、一维数组、二维数组。 1 数组 1.1 概述…

python web 医院加密处方系统

医院加密处方系统 环境要求&#xff1a; 1、python3.8 2、vue 3、django 4、mysql 5、ruoyi快速开发框架 登录界面 可以登录和注册&#xff0c;注册分三个角色&#xff0c;主治医师和药品医师还有配制医师&#xff0c;有验证码和用户权限功能&#xff0c;用户管理、部…

AIGC下一站:期待、警惕充斥着AI剪辑师的世界

上月底&#xff0c;名为“chaindrop”的 Reddit 用户&#xff0c;在 r/StableDiffusion subreddit 上分享了一个由人工智能生成的视频&#xff0c;在业内引起了不小的争议。 视频中&#xff0c;一个由 AI 生成的丑陋畸形的 “威尔史密斯”&#xff0c;以一种可怕的热情将一把意…

vba:消息框基础,massagebox

常量常量值说明vbOKOnly0只显示“确定”按钮&#xff08;缺省值&#xff09;VbOKCancel1显示“确定”和“取消”按钮VbAbortRetryIgnore2显示“终止”、“重试”和“忽略” 按钮VbYesNoCancel3显示“是”、“否”和“取消”按钮VbYesNo4显示“是”和“否”按钮VbRetryCancel5显…

pkg-config

前言 在介绍 pkg-config 之前&#xff0c;先讲一个我的经历。 有一次我想用 libgtk 库在 ubuntu 上实现一个简单的图形界面&#xff0c;就像下面代码 #include <gtk/gtk.h>int main(int argc, char *argv[]) {GtkWidget *window;gtk_init(&argc, &argv);window…