Type Hints in Python:python的类型注释

个人博客:Sekyoro的博客小屋
个人网站:Proanimer的个人网站

Python中的类型系统,使用type hints使得整个开发过程更加顺畅.类似typescript的目的.

Type Theory

值得一提的是python目前还在蒸蒸日上,所以一些东西后面可能会有些改变,不过答题的东西是不变的,可以使用mypypython/mypy: Optional static typing for Python (github.com)(或者pyrightmicrosoft/pyright: Static Type Checker for Python (github.com))进行检查,可以使用Welcome to Pydantic - Pydantic作为数据验证,大多数IDE本身也对这个默认支持.

PEP 483 是这一切的起点.

Subtypes

一个重要的概念是subtypes(亚型)。

形式上,如果以下两个条件成立,我们说T型是U的subtypes:

  • 来自T的每个值也在U类型的值集合中。
  • 来自U型的每个函数也在T型函数的集合中。

这两个条件保证了即使类型T与U不同,类型T的变量也可以总是假装为U。

举个具体的例子,考虑T=bool和U=int。bool类型只取两个值。通常这些名称表示为True和False,但这些名称分别只是整数值1和0的别名:

Covariant, Contravariant, and Invariant

在复合类型中使用子类型时会发生什么?例如,Tuple[bool]是Tuple[int]的一个子类型吗?答案取决于复合类型,以及该类型是协变(Covariant)的、反变(Contravariant)的还是不变(Invariant)的。

  • 元组是协变(Covariant)的。这意味着它保留了其项类型的类型层次结构:Tuple[bool]是Tuple[int]的子类型,因为bool是int的子类型。
  • 列表是不变(Invariant)的。不变类型不能保证子类型。虽然List[bool]的所有值都是List[int]的值,但您可以将int附加到List[int],而不是List[bool。换句话说,子类型的第二个条件不成立,并且List[bool]不是List[int]的子类型。
  • Callable在其参数中是反变(Contravariant)的。这意味着它颠倒了类型层次结构。若Callable[[T],…]作为一个函数,它唯一的参数是T类型。Callable的一个例子[[int],…]是double()函数。反变意味着,如果期望一个在布尔上操作的函数,那么一个在int上操作的功能是可以接受的。

内置类型

x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"

在3.8及之前,使用from typing import List,Dict,Set,Tuple 来使用collections,之后可以直接使用list,dict这种.

x: list[int] = []
x: tuple[int,...] = (1, 2)
x: set[int] = {1, 2}
x: dict[str, float] = {"field": 2.0, "field2": "a"}

在3.10+,可以直接使用|代替Union

x: list[int|str] = [1, 2, "a"]
x: Optional[str]

函数

x: Callable[[int], str] = stringify
def gen(n: int) -> Iterator[int]:for i in range(n):yield idef send_email(address: Union[str,list[str],None]) -> None:...
# This says each positional arg and each keyword arg is a "str"
def call(self, *args: str, **kwargs: str) -> str:reveal_type(args)  # Revealed type is "tuple[str, ...]"reveal_type(kwargs)  # Revealed type is "dict[str, str]"request = make_request(*args, **kwargs)return self.do_api_query(request)
def quux(x: int,/, y: str, z: float) -> None:...quux(1, '2', z=3.0)

如果你想要函数的调用者在某个参数位置只能使用位置参数而不能使用关键字参数传参,那么你只需要在所需位置后面放置一个/。

如果你希望强迫调用者使用某些参数,且必须以关键字参数的形式传参,那么你只需要在所需位置的前一个位置放置一个*。

from typing import ClassVarclass BankAccount:account_name: strbalance: floatcount: ClassVardef __init__(self, account_name: str, initial_balance: float = 0.0) -> None:self.account_name = account_nameself.balance = initial_balancedef deposit(self, amount: float) -> None:self.balance += amountdef withdraw(self, amount: float) -> None:self.balance -= amount
class AuditedBankAccount(BankAccount):audit_log: list[str]def __init__(self, account_name: str, initial_balance: float = 0.0) -> None:super().__init__(account_name, initial_balance)self.audit_log = []def deposit(self, amount: float) -> None:self.audit_log.append(f"Deposited {amount}")def withdraw(self, amount: float) -> None:self.audit_log.append(f"Withdrew {amount}")# You can use the ClassVar annotation to declare a class variable
class Car:seats: ClassVar[int] = 4passengers: ClassVar[list[str]]class A:def __setattr__(self, key, value):print("Setting", key, "to", value)self.__dict__[key] = valuedef __getattr__(self, key):print("Getting", key)return self.__dict__[key]class Person(A):name: strage: intweight: floatdef __init__(self, name: str, age: int, weight: float) -> None:self.name = nameself.age = ageself.weight = weightp = Person("John", 30, 80.0)
print(p.name)

Forward references

# You may want to reference a class before it is defined.
# This is known as a "forward reference".
def f(foo: A) -> int:  # This will fail at runtime with 'A' is not defined...# However, if you add the following special import:
from __future__ import annotations
# It will work at runtime and type checking will succeed as long as there
# is a class of that name later on in the file
def f(foo: A) -> int:  # Ok...# Another option is to just put the type in quotes
def f(foo: 'A') -> int:  # Also ok...class A:# This can also come up if you need to reference a class in a type# annotation inside the definition of that class@classmethoddef create(cls) -> A:...

Decorators

decorator通常是将一个函数作为参数并返回另一个函数的函数。

用类型来描述这种行为可能有点棘手;我们将展示如何使用TypeVar和一种称为参数规范的特殊类型变量来实现这一点。

假设我们有装饰器,尚未进行类型注释,它保留了原始函数的签名,只打印装饰函数的名称:

def printing_decorator(func):def wrapper(*args, **kwds):print("Calling", func)return func(*args, **kwds)return wrapper

给这个装饰器类型注释

from functools import wraps
from typing import TypeVar, Callable, cast, Any
F = TypeVar("F", bound=Callable[..., Any])
def printing_decorator(func: F) -> F:@wraps(func)def wrapper(*args: Any, **kwargs: Any) -> Any:print("Calling", func.__name__)return func(*args, **kwargs)return cast(F, wrapper)

这仍然存在一些不足。首先,我们需要使用不安全的cast()来说服mypy wrapper()与func具有相同的签名。其次,wrapper()函数没有经过严格的类型检查,尽管wrapper函数通常足够小,所以这不是什么大问题。

from typing import Callable, TypeVar
from typing_extensions import ParamSpecP = ParamSpec('P')
T = TypeVar('T')def printing_decorator(func: Callable[P, T]) -> Callable[P, T]:def wrapper(*args: P.args, **kwds: P.kwargs) -> T:print("Calling", func)return func(*args, **kwds)return wrapper

可以使用参数规范(ParamSpec)来获得更好的类型注释:

from typing import TypeVar, Callable, Any,ParamSpec
P = ParamSpec("P")
T = TypeVar('T')
def printing_decorator(func: Callable[P,T]) -> Callable[P,T]:@wraps(func)def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:print("Calling", func.__name__)return func(*args, **kwargs)return wrapper

参数规范还允许描述更改输入函数签名的装饰器

from typing import Callable, TypeVar
from typing_extensions import Concatenate, ParamSpecP = ParamSpec('P')
T = TypeVar('T')# We reuse 'P' in the return type, but replace 'T' with 'str'
def stringify(func: Callable[P, T]) -> Callable[P, str]:def wrapper(*args: P.args, **kwds: P.kwargs) -> str:return str(func(*args, **kwds))return wrapper@stringifydef add_forty_two(value: int) -> int:return value + 42a = add_forty_two(3)reveal_type(a)      # Revealed type is "builtins.str"add_forty_two('x')  # error: Argument 1 to "add_forty_two" has incompatible type "str"; expected "int"P = ParamSpec('P')
T = TypeVar('T')def printing_decorator(func: Callable[P, T]) -> Callable[Concatenate[str, P], T]:def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T:print("Calling", func, "with", msg)return func(*args, **kwds)return wrapper@printing_decorator
def add_forty_two(value: int) -> int:return value + 42a = add_forty_two('three', 3)
from typing import Any, Callable, TypeVarF = TypeVar('F', bound=Callable[..., Any])def bare_decorator(func: F) -> F:...def decorator_args(url: str) -> Callable[[F], F]:...

Generics

内置集合类是泛型类。泛型类型有一个或多个类型参数,这些参数可以是任意类型。例如,dict[int,str]具有类型参数int和str,list[int]具有类型形参int。

from typing import TypeVar, GenericT = TypeVar('T')class Stack(Generic[T]):def __init__(self) -> None:# Create an empty list with items of type Tself.items: list[T] = []def push(self, item: T) -> None:self.items.append(item)def pop(self) -> T:return self.items.pop()def empty(self) -> bool:return not self.items

类ClassName(Protocol[T])被允许作为类ClassName的简写class ClassName(Protocol, Generic[T])

TypedDict

Python程序经常使用带有字符串键的字典来表示对象。TypedDict允许您为表示具有固定架构的对象的字典提供精确的类型,例如{‘id’:1,‘items’:〔‘x’〕}。

from typing import TypedDict
Movie = TypedDict('Movie', {'name': str, 'year': int})movie: Movie = {'name': 'Blade Runner', 'year': 1982}
class Movie(TypedDict):name: stryear: intclass BookBasedMovie(Movie):based_on: str

Literal

Literal类型可以指示表达式等于某个特定的primitive 值。

例如,如果我们用Literal[“foo”]类型注释一个变量,mypy将理解该变量不仅是str类型的,而且具体地等于字符串“foo”。

from typing import Final, Literaldef expects_literal(x: Literal[19]) -> None: passreveal_type(19)
expects_literal(19)

更多类型

  • NoReturn可以告诉mypy函数永远不会正常返回。
  • NewType允许您定义类型的变体,该变体被mypy视为单独的类型,但在运行时与原始类型相同。例如,您可以将UserId作为int的一个变体,它在运行时只是一个int。
  • @overload允许您定义一个可以接受多个不同签名的函数。如果您需要对难以正常表达的参数和返回类型之间的关系进行编码,这将非常有用。
  • Async 类型允许您使用异步和等待来键入检查程序。
from typing import NoReturndef stop() -> NoReturn:raise Exception('no way')from typing import NewTypeUserId = NewType('UserId', int)def name_by_id(user_id: UserId) -> str:...UserId('user')          # Fails type checkname_by_id(42)          # Fails type check
name_by_id(UserId(42))  # OKnum: int = UserId(5) + 1

参考

  1. Python Type Checking (Guide) – Real Python
  2. Type hints cheat sheet - mypy 1.7.1 documentation
  3. https://python-type-challenges.zeabur.app/

如有疑问,欢迎各位交流!

服务器配置
宝塔:宝塔服务器面板,一键全能部署及管理
云服务器:阿里云服务器
Vultr服务器
GPU服务器:Vast.ai

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

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

相关文章

(C++)移动零--双指针法

个人主页:Lei宝啊 愿所有美好如期而遇 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://le…

Web安全漏洞分析-XSS(下)

随着互联网的迅猛发展,Web应用的普及程度也愈发广泛。然而,随之而来的是各种安全威胁的不断涌现,其中最为常见而危险的之一就是跨站脚本攻击(Cross-Site Scripting,简称XSS)。XSS攻击一直以来都是Web安全领…

ntopng如何将漏洞扫描与流量监控相结合,以提高网络安全性

来源:艾特保IT 虹科干货 | ntopng如何将漏洞扫描与流量监控相结合,以提高网络安全性 欢迎关注虹科,为您提供最新资讯! ntopng为人所知的“身份”是被动流量监控。然而,如今的ntopng6.0也进化出主动监控功能来&#xf…

人工智能原理复习--知识表示(二)

文章目录 上一篇产生式表示法推理方式 结构化表示语义网络语义网络表示知识的方法和步骤应用题目 框架表示法下一篇 上一篇 人工智能原理复习–知识表示(一) 产生式表示法 把推理和行为的过程用产生式规则表示,所以又称基于规则的系统。 产…

CI/CD 最佳实践,让研发团队效率起飞!

目录 CI/CD 是什么? CI/CD 有哪些最佳实践? 深入研究 CD 的最佳实践 如何改进 CI/CD 流水线? 如何衡量 CI/CD 是否成功? 遵循 CI/CD 最佳实践有哪些收益? CI/CD 部署策略 如何在组织中实施 CI/CD? …

企业微信协议开发,API接口调用

产品说明 一、 hook版本:企业微信hook接口是指将企业微信的功能封装成dll,并提供简易的接口给程序调用。通过hook技术,可以在不修改企业微信客户端源代码的情况下,实现对企业微信客户端的功能进行扩展和定制化。企业微信hook接口…

支持向量机,硬间隔,软间隔,核技巧,超参数设置,分类与回归

SVM(Support Vector Machine,支持向量机)是一种非常常用并且有效的监督学习算法,在许多领域都有广泛应用。它可以用于二分类问题和多分类问题,并且在处理高维数据和特征选择方面非常强大。SVM算法的核心思想是通过找到…

【刷题】双指针

双指针 76. 最小覆盖子串 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。 注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t …

C++this指针与静态成员函数的使用方法

this指针 在C中,this指针是一个非常重要的概念,它指向当前对象,可以在成员函数中使用,以访问对象的非静态成员变量和非静态成员函数。而静态成员函数则是一个与类关联的函数,而不是与类的实例关联的函数。 下面分别对…

在PyCharm中运行OpenCV

一、安装Anaconda配置python环境 这里选用清华大学开源软件镜像站:anaconda | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 下载的速度更快。 点击下载链接:Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsin…

linux磁盘的分区,格式化文件系统,以及挂载

目录 磁盘 介绍 机械 HDD 固态SSD 磁盘分区: 磁盘分区类型 MBR分区方式 GPT分区方式 制作文件系统 挂载磁盘 文件类型 磁盘命令 磁盘分区类型切换 mkfs:格式化文件系统操作 mount:挂载文件系统到目录 umount :取消…

统信桌面版arm系统安装火狐浏览器和浏览器驱动

一、系统信息 二、下载浏览器和驱动 1、浏览器 https://security.debian.org/debian-security/pool/updates/main/f/firefox-esr/firefox-esr_115.5.0esr-1~deb10u1_arm64.deb 2、驱动 https://github.com/mozilla/geckodriver/releases geckodriver-v0.33.0-linux-aarch6…

鸿蒙【HarmonyOS】开发初体验

官方开发文档 依照官方开发文档进行配置,官方的文档很详细(虽然有些粗糙)。 其实只要下载了deveco studio,其他就按照next来就行了。配置都很清楚。 顺便提一下,deveco是基于intellij 的,体验很不错&…

什么是供应链攻击?

随着企业越来越依赖技术、连接性和第三方,供应链攻击变得越来越普遍。这些攻击旨在通过供应商和业务合作伙伴损害公司。 供应链攻击可能对企业和组织构成重大威胁,损害其安全以及向客户提供的产品和服务的安全。 在本文中,我们将探讨供应链…

springboot 2.x集成h2数据库

1.引入pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency><groupId>com.h2database</groupId><artifactId>h2<…

gRPC之grpc负载均衡(resolver)

1、grpc负载均衡(resolver) 1.1 proto编写和编译 syntax "proto3";option go_package "./;echo";package echo;message EchoRequest {string message 1; }message EchoResponse {string message 1; }service Echo {rpc UnaryEcho(EchoRequest) retur…

C# Thread.Sleep 与 await Task.Delay 的本质区别,和异步方法中踩到的坑。

主要区别 Thread.Sleep&#xff1a; 挂起当前线程一段时间。 await Task.Delay&#xff1a; 将当前执行代码的线程空出来&#xff0c;优先去执行其他任务。 在等待时间结束后&#xff0c;交给线程池中的线程接继续执行。 这意味着&#xff0c;经过该方法后&#xff0c;执行当…

Pytest 的小例子

一个简单的例子 下面代码保存到test_pytest.py 一个简单的例子 def inc(x):return x 1def test_answer():assert inc(3) 5def test_ask():assert inc(4) 5 pytest 需要安装一下 pip install pytest (Venv) D:\pythonwork>pip install pytest Collecting pytestDown…

【Java】JDK命令使用总结

目录 1. javac 2. java 1. javac 将源码(*.java)编译成字节码(*.class) javac HelloWorld.java 2. java 运行字节码(*.class)。注意不能加后缀名。 java HelloWorld 直接运行单文件源码(*.java)。注意Java11以上才支持。 java HelloWorld.java

《数据结构、算法与应用C++语言描述》-优先级队列-大根堆的C++实现

优先级队列 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_25Priority queue 定义 优先级队列&#xff08;priority queue&#xff09;是0个或多个元素的集合&#xff0c;每个元素都有一个优先权或值&#xff0c;对优先级队列执行…