- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Python Type Hints 学习之从入门到实践由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Python 想必大家都已经很熟悉了,甚至关于它有用或者无用的论点大家可能也已经看腻了。但是无论如何,它作为一个广受关注的语言还是有它独到之处的,今天我们就再展开聊聊 Python.
Python 是一门动态强类型语言 。
《流畅的 Python》一书中提到,如果一门语言很少隐式转换类型,说明它是强类型语言,例如 Java、C++ 和 Python 就是强类型语言.
Python 的强类型体现 。
同时如果一门语言经常隐式转换类型,说明它是弱类型语言,PHP、JavaScript 和 Perl 是弱类型语言.
动态弱类型语言:JavaScript 。
当然上面这种简单的示例对比,并不能确切的说 Python 是一门强类型语言,因为 Java 同样支持 integer 和 string 相加操作,且 Java 是强类型语言。因此《流畅的 Python》一书中还有关于静态类型和动态类型的定义:在编译时检查类型的语言是静态类型语言,在运行时检查类型的语言是动态类型语言。静态语言需要声明类型(有些现代语言使用类型推导避免部分类型声明).
综上所述,关于 Python 是动态强类型语言是比较显而易见没什么争议的.
Type Hints 初探 。
Python 在 PEP 484(Python Enhancement Proposals,Python 增强建议书)[https://www.python.org/dev/peps/pep-0484/]中提出了 Type Hints(类型注解)。进一步强化了 Python 是一门强类型语言的特性,它在 Python3.5 中第一次被引入。使用 Type Hints 可以让我们编写出带有类型的 Python 代码,看起来更加符合强类型语言风格.
这里定义了两个 greeting 函数:
普通的写法如下:
name = "world"def greeting(name): return "Hello " + namegreeting(name)
加入了 Type Hints 的写法如下:
name: str = "world"def greeting(name: str) -> str: return "Hello " + namegreeting(name)
以 PyCharm 为例,在编写代码的过程中 IDE 会根据函数的类型标注,对传递给函数的参数进行类型检查。如果发现实参类型与函数的形参类型标注不符就会有如下提示:
常见数据结构的 Type Hints 写法 。
上面通过一个 greeting 函数展示了 Type Hints 的用法,接下来我们就 Python 常见数据结构的 Type Hints 写法进行更加深入的学习.
默认参数 。
Python 函数支持默认参数,以下是默认参数的 Type Hints 写法,只需要将类型写到变量和默认参数之间即可.
def greeting(name: str = "world") -> str: return "Hello " + namegreeting()
自定义类型 。
对于自定义类型,Type Hints 同样能够很好的支持。它的写法跟 Python 内置类型并无区别.
class Student(object): def __init__(self, name, age): self.name = name self.age = agedef student_to_string(s: Student) -> str: return f"student name: {s.name}, age: {s.age}."student_to_string(Student("Tim", 18))
当类型标注为自定义类型时,IDE 也能够对类型进行检查.
容器类型 。
当我们要给内置容器类型添加类型标注时,由于类型注解运算符 [] 在 Python 中代表切片操作,因此会引发语法错误。所以不能直接使用内置容器类型当作注解,需要从 typing 模块中导入对应的容器类型注解(通常为内置类型的首字母大写形式).
from typing import List, Tuple, Dictl: List[int] = [1, 2, 3]t: Tuple[str, ...] = ("a", "b")d: Dict[str, int] = { "a": 1, "b": 2,}
不过 PEP 585[https://www.python.org/dev/peps/pep-0585/]的出现解决了这个问题,我们可以直接使用 Python 的内置类型,而不会出现语法错误.
l: list[int] = [1, 2, 3]t: tuple[str, ...] = ("a", "b")d: dict[str, int] = { "a": 1, "b": 2,}
类型别名 。
有些复杂的嵌套类型写起来很长,如果出现重复,就会很痛苦,代码也会不够整洁.
config: list[tuple[str, int], dict[str, str]] = [ ("127.0.0.1", 8080), { "MYSQL_DB": "db", "MYSQL_USER": "user", "MYSQL_PASS": "pass", "MYSQL_HOST": "127.0.0.1", "MYSQL_PORT": "3306", },]def start_server(config: list[tuple[str, int], dict[str, str]]) -> None: ...start_server(config)
此时可以通过给类型起别名的方式来解决,类似变量命名.
Config = list[tuple[str, int], dict[str, str]]config: Config = [ ("127.0.0.1", 8080), { "MYSQL_DB": "db", "MYSQL_USER": "user", "MYSQL_PASS": "pass", "MYSQL_HOST": "127.0.0.1", "MYSQL_PORT": "3306", },]def start_server(config: Config) -> None: ...start_server(config)
这样代码看起来就舒服多了.
可变参数 。
Python 函数一个非常灵活的地方就是支持可变参数,Type Hints 同样支持可变参数的类型标注.
def foo(*args: str, **kwargs: int) -> None: ...foo("a", "b", 1, x=2, y="c")
IDE 仍能够检查出来.
泛型 。
使用动态语言少不了泛型的支持,Type Hints 针对泛型也提供了多种解决方案.
TypeVar 。
使用 TypeVar 可以接收任意类型.
from typing import TypeVarT = TypeVar("T")def foo(*args: T, **kwargs: T) -> None: ...foo("a", "b", 1, x=2, y="c")
Union 。
如果不想使用泛型,只想使用几种指定的类型,那么可以使用 Union 来做。比如定义 concat 函数只想接收 str 或 bytes 类型.
from typing import UnionT = Union[str, bytes]def concat(s1: T, s2: T) -> T: return s1 + s2concat("hello", "world")concat(b"hello", b"world")concat("hello", b"world")concat(b"hello", "world")
IDE 的检查提示如下图:
TypeVar 和 Union 区别 。
TypeVar 不只可以接收泛型,它也可以像 Union 一样使用,只需要在实例化时将想要指定的类型范围当作参数依次传进来来即可。跟 Union 不同的是,使用 TypeVar 声明的函数,多参数类型必须相同,而 Union 不做限制.
from typing import TypeVarT = TypeVar("T", str, bytes)def concat(s1: T, s2: T) -> T: return s1 + s2concat("hello", "world")concat(b"hello", b"world")concat("hello", b"world")
以下是使用 TypeVar 做限定类型时的 IDE 提示:
Optional 。
Type Hints 提供了 Optional 来作为 Union[X, None] 的简写形式,表示被标注的参数要么为 X 类型,要么为 None,Optional[X] 等价于 Union[X, None].
from typing import Optional, Union# None => type(None)def foo(arg: Union[int, None] = None) -> None: ...def foo(arg: Optional[int] = None) -> None: ...
Any 。
Any 是一种特殊的类型,可以代表所有类型。未指定返回值与参数类型的函数,都隐式地默认使用 Any,所以以下两个 greeting 函数写法等价:
from typing import Optional, Union# None => type(None)def foo(arg: Union[int, None] = None) -> None: ...def foo(arg: Optional[int] = None) -> None: ...
当我们既想使用 Type Hints 来实现静态类型的写法,也不想失去动态语言特有的灵活性时,即可使用 Any.
Any 类型值赋给更精确的类型时,不执行类型检查,如下代码 IDE 并不会有错误提示:
from typing import Anya: Any = Nonea = [] # 动态语言特性a = 2s: str = ""s = a # Any 类型值赋给更精确的类型
可调用对象(函数、类等) 。
Python 中的任何可调用类型都可以使用 Callable 进行标注。如下代码标注中 Callable[[int], str],[int] 表示可调用类型的参数列表,str 表示返回值.
from typing import Callabledef int_to_str(i: int) -> str: return str(i)def f(fn: Callable[[int], str], i: int) -> str: return fn(i)f(int_to_str, 2)
自引用 。
当我们需要定义树型结构时,往往需要自引用。当执行到 __init__ 方法时 Tree 类型还没有生成,所以不能像使用 str 这种内置类型一样直接进行标注,需要采用字符串形式“Tree”来对未生成的对象进行引用.
class Tree(object): def __init__(self, left: "Tree" = None, right: "Tree" = None): self.left = left self.right = righttree1 = Tree(Tree(), Tree())
IDE 同样能够对自引用类型进行检查.
此形式不仅能够用于自引用,前置引用同样适用.
鸭子类型 。
Python 一个显著的特点是其对鸭子类型的大量应用,Type Hints 提供了 Protocol 来对鸭子类型进行支持。定义类时只需要继承 Protocol 就可以声明一个接口类型,当遇到接口类型的注解时,只要接收到的对象实现了接口类型的所有方法,即可通过类型注解的检查,IDE 便不会报错。这里的 Stream 无需显式继承 Interface 类,只需要实现了 close 方法即可.
from typing import Protocolclass Interface(Protocol): def close(self) -> None: ...# class Stream(Interface):class Stream: def close(self) -> None: ...def close_resource(r: Interface) -> None: r.close()f = open("a.txt")close_resource(f)s: Stream = Stream()close_resource(s)
由于内置的 open 函数返回的文件对象和 Stream 对象都实现了 close 方法,所以能够通过 Type Hints 的检查,而字符串“s”并没有实现 close 方法,所以 IDE 会提示类型错误.
Type Hints 的其他写法 。
实际上 Type Hints 不只有一种写法,Python 为了兼容不同人的喜好和老代码的迁移还实现了另外两种写法.
使用注释编写 。
来看一个 tornado 框架的例子(tornado/web.py)。适用于在已有的项目上做修改,代码已经写好了,后期需要增加类型标注.
使用单独文件编写(.pyi) 。
可以在源代码相同的目录下新建一个与 .py 同名的 .pyi 文件,IDE 同样能够自动做类型检查。这么做的优点是可以对原来的代码不做任何改动,完全解耦。缺点是相当于要同时维护两份代码.
Type Hints 实践 。
基本上,日常编码中常用的 Type Hints 写法都已经介绍给大家了,下面就让我们一起来看看如何在实际编码中中应用 Type Hints.
dataclass——数据类 。
dataclass 是一个装饰器,它可以对类进行装饰,用于给类添加魔法方法,例如 __init__() 和 __repr__() 等,它在 PEP 557中被定义.
from dataclasses import dataclass, field@dataclassclass User(object): id: int name: str friends: list[int] = field(default_factory=list)data = { "id": 123, "name": "Tim",}user = User(**data)print(user.id, user.name, user.friends)# > 123 Tim []
以上使用 dataclass 编写的代码同如下代码等价:
class User(object): def __init__(self, id: int, name: str, friends=None): self.id = id self.name = name self.friends = friends or []data = { "id": 123, "name": "Tim",}user = User(**data)print(user.id, user.name, user.friends)# > 123 Tim []
注意:dataclass 并不会对字段类型进行检查.
可以发现,使用 dataclass 来编写类可以减少很多重复的样板代码,语法上也更加清晰.
Pydantic 。
Pydantic 是一个基于 Python Type Hints 的第三方库,它提供了数据验证、序列化和文档的功能,是一个非常值得学习借鉴的库。以下是一段使用 Pydantic 的示例代码:
from datetime import datetimefrom typing import Optionalfrom pydantic import BaseModelclass User(BaseModel): id: int name = "John Doe" signup_ts: Optional[datetime] = None friends: list[int] = []external_data = { "id": "123", "signup_ts": "2021-09-02 17:00", "friends": [1, 2, "3"],}user = User(**external_data)print(user.id)# > 123print(repr(user.signup_ts))# > datetime.datetime(2021, 9, 2, 17, 0)print(user.friends)# > [1, 2, 3]print(user.dict())"""{ "id": 123, "signup_ts": datetime.datetime(2021, 9, 2, 17, 0), "friends": [1, 2, 3], "name": "John Doe",}"""
注意:Pydantic 会对字段类型进行强制检查.
Pydantic 写法上跟 dataclass 非常类似,但它做了更多的额外工作,还提供了如 .dict() 这样非常方便的方法.
再来看一个 Pydantic 进行数据验证的示例,当 User 类接收到的参数不符合预期时,会抛出 ValidationError 异常,异常对象提供了 .json() 方法方便查看异常原因.
from pydantic import ValidationErrortry: User(signup_ts="broken", friends=[1, 2, "not number"])except ValidationError as e: print(e.json())"""[ { "loc": [ "id" ], "msg": "field required", "type": "value_error.missing" }, { "loc": [ "signup_ts" ], "msg": "invalid datetime format", "type": "value_error.datetime" }, { "loc": [ "friends", 2 ], "msg": "value is not a valid integer", "type": "type_error.integer" }]"""
所有报错信息都保存在一个 list 中,每个字段的报错又保存在嵌套的 dict 中,其中 loc 标识了异常字段和报错位置,msg 为报错提示信息,type 则为报错类型,这样整个报错原因一目了然.
MySQLHandler 。
MySQLHandler[https://github.com/jianghushinian/python-scripts/blob/main/scripts/mysql_handler_type_hints.py]是我对 pymysql 库的封装,使其支持使用 with 语法调用 execute 方法,并且将查询结果从 tuple 替换成 object,同样也是对 Type Hints 的应用.
class MySQLHandler(object): """MySQL handler""" def __init__(self): self.conn = pymysql.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASS, database=DB_NAME, charset=DB_CHARSET, client_flag=CLIENT.MULTI_STATEMENTS, # execute multi sql statements ) self.cursor = self.conn.cursor() def __del__(self): self.cursor.close() self.conn.close() @contextmanager def execute(self): try: yield self.cursor.execute self.conn.commit() except Exception as e: self.conn.rollback() logging.exception(e) @contextmanager def executemany(self): try: yield self.cursor.executemany self.conn.commit() except Exception as e: self.conn.rollback() logging.exception(e) def _tuple_to_object(self, data: List[tuple]) -> List[FetchObject]: obj_list = [] attrs = [desc[0] for desc in self.cursor.description] for i in data: obj = FetchObject() for attr, value in zip(attrs, i): setattr(obj, attr, value) obj_list.append(obj) return obj_list def fetchone(self) -> Optional[FetchObject]: result = self.cursor.fetchone() return self._tuple_to_object([result])[0] if result else None def fetchmany(self, size: Optional[int] = None) -> Optional[List[FetchObject]]: result = self.cursor.fetchmany(size) return self._tuple_to_object(result) if result else None def fetchall(self) -> Optional[List[FetchObject]]: result = self.cursor.fetchall() return self._tuple_to_object(result) if result else None
运行期类型检查 。
Type Hints 之所以叫 Hints 而不是 Check,就是因为它只是一个类型的提示而非真正的检查。上面演示的 Type Hints 用法,实际上都是 IDE 在帮我们完成类型检查的功能,但实际上,IDE 的类型检查并不能决定代码执行期间是否报错,仅能在静态期做到语法检查提示的功能.
要想实现在代码执行阶段强制对类型进行检查,则需要我们通过自己编写代码或引入第三方库的形式(如上面介绍的 Pydantic)。下面我通过一个 type_check 函数实现了运行期动态检查类型,来供你参考:
from inspect import getfullargspecfrom functools import wrapsfrom typing import get_type_hintsdef type_check(fn): @wraps(fn) def wrapper(*args, **kwargs): fn_args = getfullargspec(fn)[0] kwargs.update(dict(zip(fn_args, args))) hints = get_type_hints(fn) hints.pop("return", None) for name, type_ in hints.items(): if not isinstance(kwargs[name], type_): raise TypeError(f"expected {type_.__name__}, got {type(kwargs[name]).__name__} instead") return fn(**kwargs) return wrapper# name: str = "world"name: int = 2@type_checkdef greeting(name: str) -> str: return str(name)print(greeting(name))# > TypeError: expected str, got int instead
只要给 greeting 函数打上 type_check 装饰器,即可实现运行期类型检查.
附录 。
如果你想继续深入学习使用 Python Type Hints,以下是一些我推荐的开源项目供你参考:
以上就是Python Type Hints 学习之从入门到实践的详细内容,更多关于Python Type Hints的资料请关注我其它相关文章! 。
原文链接:https://blog.csdn.net/weixin_38037405/article/details/121486274 。
最后此篇关于Python Type Hints 学习之从入门到实践的文章就讲到这里了,如果你想了解更多关于Python Type Hints 学习之从入门到实践的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
假设a是张量,那么有什么区别: 类型(a) a.类型 a.type() 我找不到区分这些的文档。 最佳答案 type 是 python 内置方法。 它将返回对象的类型。喜欢 torch.Tensor.
什么是 Type 1 的居民的例子?两者都不是 Type也不是Type的居民?在 Idris REPL 中进行探索时,我无法想出任何东西。 更准确地说,我正在寻找一些 x除了 Type产生以下结果:
我找到了一些资源,但我不确定我是否理解。 我找到的一些资源是: http://help.sap.com/saphelp_nw70/helpdata/en/fc/eb2ff3358411d1829f00
这两个函数原型(prototype)有什么区别? void apply1(double(f)(double)); void apply2(double(*f)(double)); 如果目标是将提供的函
http://play.golang.org/p/icQO_bAZNE 我正在练习使用堆进行排序,但是 prog.go:85: type bucket is not an expression
假设有一个泛型定义的方法信息对象,即一个方法信息对象,这样的方法Info.IsGenericMethodDefinition==TRUE:。也可以说它们也有一个泛型参数列表:。我可以使用以下命令获取该
在具有依赖类型的语言中,您可以使用 Type-in-Type 来简化语言并赋予它很多功能。这使得语言在逻辑上不一致,但如果您只对编程感兴趣而不对定理证明感兴趣,这可能不是问题。 在 Cayenne
根据 Nim 手册,变量类型是“静态类型”,而变量在内存中指向的实际值是“动态类型”。 它们怎么可能是不同的类型?我认为将错误的类型分配给变量将是一个错误。 最佳答案 import typetrait
假设您有以下结构和协议(protocol): struct Ticket { var items: [TicketItem] = [] } struct TicketItem { } prot
我正在处理一个 EF 问题,我发现它很难调试...以前,在我的系统中有一个表类型继承设置管理不同的用户类型 - 所有用户共有的一种根类型,以及大致基于使用该帐户的人员类型的几种不同的子类型。现在,我遇
这是我的 DBManager.swift import RealmSwift class DBManager { class func getAllDogs() -> [Dog] {
我正在尝试使用傅里叶校正图像中的曝光。这是我面临的错误 5 padded = np.log(padded + 1) #so we never have log of 0 6 g
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improve
请考虑以下设置: protocol MyProcotol { } class MyModel: MyProcotol { } enum Result { case success(value:
好吧,我将我的 python 项目编译成一个可执行文件,它在我的电脑上运行,但我将它发送给几个 friend 进行测试,他们都遇到了这个错误。我以前从未见过这样的错误。我使用 Nuitka 来编译代码
当我尝试训练我的模型时"ValueError: Type must be a sub-type of ndarray type"出现在 line x_norm=(np.power(x,2)).sum(
我尝试在另一个类中打断、计数然后加入对象。所以我构建协议(protocol): typealias DataBreaker = () -> [Double] typealias DataJoiner
我正在使用 VS 2015 更新 3、Angular 2.1.2、Typescript 2.0.6 有人可以澄清什么是 typings 与 npm @types 以及本月很难找到的任何其他文档吗? 或
我正在考虑从 VS2010 更改为 Mono,因此我通过 MoMA 运行我的程序集,看看我在转换过程中可能遇到多少困难。在生成的报告中,我发现我不断收到此错误: bool Type.op_Equali
主要问题 不太确定这是否可能,但由于我讨厌 Typescript 并且它使我的编码变得困难,我想我会问只是为了确定。 interface ISomeInterface { handler: ()
我是一名优秀的程序员,十分优秀!