Python标准库之typing的用法(类型标注)


Posted in Python onJune 02, 2021

PEP 3107引入了功能注释的语法,PEP 484 加入了类型检查

标准库 typing 为类型提示指定的运行时提供支持。

示例:

def f(a: str, b:int) -> str:
    return a * b

Python标准库之typing的用法(类型标注)

如果实参不是预期的类型:

Python标准库之typing的用法(类型标注)

但是,Python运行时不强制执行函数和变量类型注释。使用类型检查器,IDE,lint等才能帮助代码进行强制类型检查。

使用NewType 创建类型

NewType() 是一个辅助函数,用于向类型检查器指示不同的类型,在运行时,它返回一个函数,该函数返回其参数。

import typing
Id = typing.NewType("Id", int)
a = Id(2020)

使用 NewType() 创建的类型会被类型检查器视为它的原始类型的子类。

回调(Callable)

将回调函数类型标注为 Callable[[Arg1Type, Arg2Type], ReturnType]。

from typing import Callable
def f(a: int) -> str:
    return str(a)
def callback(a: int, func: Callable[[int], str]) -> str:
    return func(a)
print(callback(1, f))

泛型

为容器元素添加预期的类型

from typing import Mapping
a: Mapping[str, str]

Python标准库之typing的用法(类型标注)

通过 TypeVar 进行参数化来约束一个类型集合:

from typing import TypeVar
T = TypeVar('T') # 可以是任何东西。
A = TypeVar('A', str, bytes) # 必须是 str 或 bytes

Python标准库之typing的用法(类型标注)

使用 TypeVar 约束一个类型集合,但不允许单个约束

例如:

T = TypeVar('T', str)

这样会抛出一个异常 TypeError: A single constraint is not allowed

typing 包含的类型

AbstractSet = typing.AbstractSet
Any = typing.Any
AnyStr = ~AnyStr
AsyncContextManager = typing.AbstractAsyncContextManager
AsyncGenerator = typing.AsyncGenerator
AsyncIterable = typing.AsyncIterable
AsyncIterator = typing.AsyncIterator
Awaitable = typing.Awaitable
ByteString = typing.ByteString
Callable = typing.Callable
ClassVar = typing.ClassVar
Collection = typing.Collection
Container = typing.Container
ContextManager = typing.AbstractContextManager
Coroutine = typing.Coroutine
Counter = typing.Counter
DefaultDict = typing.DefaultDict
Deque = typing.Deque
Dict = typing.Dict
FrozenSet = typing.FrozenSet
Generator = typing.Generator
Hashable = typing.Hashable
ItemsView = typing.ItemsView
Iterable = typing.Iterable
Iterator = typing.Iterator
KeysView = typing.KeysView
List = typing.List
Mapping = typing.Mapping
MappingView = typing.MappingView
MutableMapping = typing.MutableMapping
MutableSequence = typing.MutableSequence
MutableSet = typing.MutableSet
NoReturn = typing.NoReturn
Optional = typing.Optional
Reversible = typing.Reversible
Sequence = typing.Sequence
Set = typing.Set
Sized = typing.Sized
TYPE_CHECKING = False
Tuple = typing.Tuple
Type = typing.Type
Union = typing.Union
ValuesView = typing.ValuesView

typing-python用于类型注解的库

简介

动态语言的灵活性使其在做一些工具,脚本时非常方便,但是同时也给大型项目的开发带来了一些麻烦。

自python3.5开始,PEP484为python引入了类型注解(type hints),虽然在pep3107定义了函数注释(function annotation)的语法,但仍然故意留下了一些未定义的行为.现在已经拥有许多对于静态类型的分析的第三方工具,而pep484引入了一个模块来提供这些工具,同时还规定一些不能使用注释(annoation)的情况

#一个典型的函数注释例子,为参数加上了类型
def greeting(name: str) -> str:
    return 'Hello ' + name

伴随着python3.6的pep526则更进一步引入了对变量类型的声明,和在以前我们只能在注释中对变量的类型进行说明

# 使用注释来标明变量类型
primes = [] # type:list[int]
captain = ... #type:str
class Starship:
    stats = {} #type:Dict[str,int]
primes:List[int] = []
captain:str #Note: no initial value
class Starship:
    stats: ClassVar[Dict[str,int]] = {}

typing--对于type hints支持的标准库

typing模块已经被加入标准库的provisional basis中,新的特性可能会增加,如果开发者认为有必要,api也可能会发生改变,即不保证向后兼容性

我们已经在简介中介绍过类型注解,那么除了默认类型的int、str用于类型注解的类型有哪些呢?

typing库便是一个帮助我们实现类型注解的库

类型别名(type alias)

在下面这个例子中,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, List
ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]
def broadcast_message(message: str, servers: List[Server]) -> None:
    ...
# The static type checker will treat the previous type signature as
# being exactly equivalent to this one.
def broadcast_message(
        message: str,
        servers: List[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    pass

新类型(New Type)

使用NewType来辅助函数创造不同的类型

form typing import NewType
UserId = NewType("UserId", int)
some_id = UserId(524313)

静态类型检查器将将新类型视为原始类型的子类。这对于帮助捕获逻辑错误非常有用

def get_user_name(user_id: UserId) -> str:
    pass
# typechecks
user_a = get_user_name(UserId(42351))
# does not typecheck; an int is not a UserId
user_b = get_user_name(-1)

你仍然可以使用int类型变量的所有操作来使用UserId类型的变量,但结果返回的都是都是int类型。例如

# output仍然是int类型而不是UserId类型
output = UserId(23413) + UserId(54341)

虽然这无法阻止你使用int类型代替UserId类型,但可以避免你滥用UserId类型

注意,这些检查仅仅被静态检查器强制检查,在运行时Derived = NewType('Derived',base)将派生出一个函数直接返回你传的任何参数,这意味着Derived(some_value)并不会创建任何新类或者创建任何消耗大于普通函数调用消耗的函数

确切地说,这个表达式 some_value is Derived(some_value) 在运行时总是对的。

这也意味着不可能创建派生的子类型,因为它在运行时是一个标识函数,而不是一个实际类型:

from typing import NewType
UserId = NewType('UserId', int)
# Fails at runtime and does not typecheck
class AdminUserId(UserId): pass

然而,它可以创建一个新的类型基于衍生的NewType

from typing import NewType
UserId = NewType('UserId', int)
ProUserId = NewType('ProUserId', UserId)

然后对于ProUserId的类型检查会如预料般工作

Note:回想一下,使用类型别名声明的两个类型是完全一样的,令Doing = Original将会使静态类型检查时把Alias等同于Original,这个结论能够帮助你简化复杂的类型声明

与Alias不同,NewType声明了另一个的子类,令Derived = NewType('Derived', Original)将会使静态类型检查把Derived看做Original的子类,这意味着类型Original不能用于类型Derived,这有助于使用最小的消耗来防止逻辑错误。

回调(callable)

回调函数可以使用类似Callable[[Arg1Type, Arg2Type],ReturnType]的类型注释

例如

from typing import Callable
def feeder(get_next_item: Callable[[], str]) -> None:
    # Body
def async_query(on_success: Callable[[int], None],
                on_error: Callable[[int, Exception], None]) -> None:
    # Body

可以通过对类型提示中的参数列表替换一个文本省略号来声明一个可调用的返回类型,而不指定调用参数,例如 Callable[..., ReturnType]

泛型(Generics)

因为容器中的元素的类型信息由于泛型不同通过一般方式静态推断,因此抽象类被用来拓展表示容器中的元素

from typing import Mapping, Sequence
def notify_by_email(employees: Sequence[Employee],
                    overrides: Mapping[str, str]) -> None: ...

可以通过typing中的TypeVar将泛型参数化

from typing import Sequence, TypeVar
T = TypeVar('T')      # 申明类型变量
def first(l: Sequence[T]) -> T:   # Generic function
    return l[0]

用户定义泛型类型

from typing import TypeVar, Generic
from logging import Logger
T = TypeVar('T')
class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value
    def set(self, new: T) -> None:
        self.log('Set ' + repr(self.value))
        self.value = new
    def get(self) -> T:
        self.log('Get ' + repr(self.value))
        return self.value
    def log(self, message: str) -> None:
        self.logger.info('%s: %s', self.name, message)

定义了Generic[T]作为LoggedVar的基类,同时T也作为了方法中的参数。

通过Generic基类使用元类(metaclass)定义__getitem__()使得LoggedVar[t]是有效类型

from typing import Iterable
def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
    for var in vars:
        var.set(0)

泛型可以是任意类型的变量,但也可以被约束

from typing import TypeVar, Generic
...
T = TypeVar('T')
S = TypeVar('S', int, str)
class StrangePair(Generic[T, S]):
    ...

每个类型变量的参数必须是不同的

下面是非法的

from typing import TypeVar, Generic
...
T = TypeVar('T')
class Pair(Generic[T, T]):   # INVALID
    ...

你可以使用Generic实现多继承

from typing import TypeVar, Generic, Sized
T = TypeVar('T')
class LinkedList(Sized, Generic[T]):
    ...

当继承泛型类时,一些类型变量可以被固定

from typing import TypeVar, Mapping
T = TypeVar('T')
class MyDict(Mapping[str, T]):
    ...

使用泛型类而不指定类型参数则假定每个位置都是Any,。在下面的例子中,myiterable不是泛型但隐式继承Iterable [Any]

from typing import Iterable
class MyIterable(Iterable): # Same as Iterable[Any]

还支持用户定义的泛型类型别名。实例:

from typing import TypeVar, Iterable, Tuple, Union
S = TypeVar('S')
Response = Union[Iterable[S], int]
# Return type here is same as Union[Iterable[str], int]
def response(query: str) -> Response[str]:
    ...
T = TypeVar('T', int, float, complex)
Vec = Iterable[Tuple[T, T]]
def inproduct(v: Vec[T]) -> T: # Same as Iterable[Tuple[T, T]]
    return sum(x*y for x, y in v)

Generic的元类是abc.ABCMeta的子类,泛型类可以是包含抽象方法或属性的ABC类(A generic class can be an ABC by including abstract methods or properties)

同时泛型类也可以含有ABC类的方法而没有元类冲突。

Any

一种特殊的类型是。静态类型检查器将将每个类型视为与任何类型和任何类型兼容,与每个类型兼容。

from typing import Any
a = None    # type: Any
a = []      # OK
a = 2       # OK
s = ''      # type: str
s = a       # OK
def foo(item: Any) -> int:
    # Typechecks; 'item' could be any type,
    # and that type might have a 'bar' method
    item.bar()
    ...

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
用Python解决计数原理问题的方法
Aug 04 Python
用python编写第一个IDA插件的实例
May 29 Python
详解Python3的TFTP文件传输
Jun 26 Python
Python在for循环中更改list值的方法【推荐】
Aug 17 Python
使用Filter过滤python中的日志输出的实现方法
Jul 17 Python
Django 简单实现分页与搜索功能的示例代码
Nov 07 Python
python yield和Generator函数用法详解
Feb 10 Python
解决Python spyder显示不全df列和行的问题
Apr 20 Python
Python 读取位于包中的数据文件
Aug 07 Python
python map比for循环快在哪
Sep 21 Python
Matlab求解数组中的最大值及它所在的具体位置
Apr 16 Python
Python借助with语句实现代码段只执行有限次
Mar 23 Python
只用50行Python代码爬取网络美女高清图片
这样写python注释让代码更加的优雅
Jun 02 #Python
上帝为你开了一扇窗之Tkinter常用函数详解
只用20行Python代码实现屏幕录制功能
TensorFlow中tf.batch_matmul()的用法
Jun 02 #Python
pytorch 运行一段时间后出现GPU OOM的问题
Jun 02 #Python
python flask开发的简单基金查询工具
You might like
php购物网站支付paypal使用方法
2010/11/28 PHP
php数组函数序列之prev() - 移动数组内部指针到上一个元素的位置,并返回该元素值
2011/10/31 PHP
Laravel使用RabbitMQ的方法示例
2019/06/18 PHP
JS 显示当前日期与时间的代码
2010/03/24 Javascript
Javascript this 的一些学习总结
2012/08/02 Javascript
jQuery类选择器用法实例
2014/12/23 Javascript
Js控制滑轮左右滑动实例
2015/02/13 Javascript
JS使用parseInt解析数字实现求和的方法
2015/08/05 Javascript
纯javascript模仿微信打飞机小游戏
2015/08/20 Javascript
详解JavaScript基于面向对象之创建对象(2)
2015/12/10 Javascript
Bootstrap实现渐变顶部固定自适应导航栏
2020/08/27 Javascript
javascript中apply/call和bind的使用
2017/02/15 Javascript
jQuery实现弹幕效果
2017/02/17 Javascript
easy ui datagrid 从编辑框中获取值的方法
2017/02/22 Javascript
Bootstrap媒体对象学习使用
2017/03/07 Javascript
Vue实现带进度条的文件拖动上传功能
2018/02/23 Javascript
vue实现图片滚动的示例代码(类似走马灯效果)
2018/03/03 Javascript
解决vue-cli创建项目的loader问题
2018/03/13 Javascript
Node.js中的cluster模块深入解读
2018/06/11 Javascript
vue中动态设置meta标签和title标签的方法
2018/07/11 Javascript
vue项目中引入Sass实例方法
2019/08/27 Javascript
vue本地打开build后生成的dist文件夹index.html问题
2019/09/04 Javascript
python 获取文件列表(或是目录例表)
2009/03/25 Python
python搭建服务器实现两个Android客户端间收发消息
2018/04/12 Python
python使用numpy读取、保存txt数据的实例
2018/10/14 Python
Python Selenium 之关闭窗口close与quit的方法
2019/02/13 Python
详解Python对JSON中的特殊类型进行Encoder
2019/07/15 Python
python内置模块collections知识点总结
2019/12/19 Python
python logging添加filter教程
2019/12/24 Python
python实现在线翻译
2020/06/18 Python
python boto和boto3操作bucket的示例
2020/10/30 Python
在html5的Canvas上绘制椭圆的几种方法总结
2013/01/07 HTML / CSS
J2EE系统只能是基于web
2015/09/08 面试题
学校食品安全实施方案
2014/06/14 职场文书
大学活动总结模板
2014/07/10 职场文书
幼儿园父亲节活动总结
2015/02/12 职场文书