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

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

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

示例:

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

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

但是,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]

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

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

使用 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()
    ...

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Python中typing模块与类型注解的使用方法

    实例引入 我们知道 Python 是一种动态语言,在声明一个变量时我们不需要显式地声明它的类型,例如下面的例子: a = 2 print('1 + a =', 1 + a) 运行结果: 1 + a = 3 这里我们首先声明了一个变量 a,并将其赋值为了 2,然后将最后的结果打印出来,程序输出来了正确的结果.但在这个过程中,我们没有声明它到底是什么类型. 但如果这时候我们将 a 变成一个字符串类型,结果会是怎样的呢?改写如下: a = '2' print('1 + a =', 1 + a) 运行结

  • 详解python3类型注释annotations实用案例

    1.类型注解简介 Python是一种动态类型化的语言,不会强制使用类型提示,但为了更明确形参类型,自python3.5开始,PEP484为python引入了类型注解(type hints) 示例如下: 2.常见的数据类型 int,long,float: 整型,长整形,浮点型 bool,str: 布尔型,字符串类型 List, Tuple, Dict, Set: 列表,元组,字典, 集合 Iterable,Iterator: 可迭代类型,迭代器类型 Generator:生成器类型 Sequence

  • Python-typing: 类型标注与支持 Any类型详解

    Any docs Any 是一种特殊的类型. 静态类型检查器将所有类型视为与 Any 兼容,反之亦然, Any 也与所有类型相兼容. 这意味着可对类型为 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

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

    PEP 3107引入了功能注释的语法,PEP 484 加入了类型检查 标准库 typing 为类型提示指定的运行时提供支持. 示例: def f(a: str, b:int) -> str: return a * b 如果实参不是预期的类型: 但是,Python运行时不强制执行函数和变量类型注释.使用类型检查器,IDE,lint等才能帮助代码进行强制类型检查. 使用NewType 创建类型 NewType() 是一个辅助函数,用于向类型检查器指示不同的类型,在运行时,它返回一个函数,该函数返回其

  • Python标准库中的logging用法示例详解

    目录 1.logging的介绍 2.简单用法示例 3.日志级别 4.打印格式的各个参数 5.日志输出到指定文件 6.日志回滚(按照文件大小滚动) 7.日志回滚(按照时间滚动) 1.logging的介绍 logging是Python标准库中记录常用的记录日志库,通过logging模块存储各种格式的日志,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等. 2.简单用法示例 首先创建一个logger.py的文件,其里面的代码如下所示: import logging # 1.创

  • Python标准库defaultdict模块使用示例

    Python标准库中collections对集合类型的数据结构进行了很多拓展操作,这些操作在我们使用集合的时候会带来很多的便利,多看看很有好处. defaultdict是其中一个方法,就是给字典value元素添加默认类型,之前看到过但是没注意怎么使用,今天特地瞅了瞅. 首先是各大文章介绍的第一个例子: 复制代码 代码如下: import collections as coll    def default_factory():      return 'default value'    d =

  • python标准库模块之json库的基础用法

    目录 前言 作用 loads,load的用法 dumps,dump的用法 结语 前言 json,全称为JavaScript Object Notation,也就是JavaScript对象标记,通过对象和数组的组合表示数据,虽然构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式. 作用 主要用于将python对象编码为json格式输出或存储,以及将json格式对象解码为python对象. 一个 JSON 对象可以写为如下形式: [{ "name": "小明",

  • Python标准库datetime之datetime模块用法分析详解

    目录 1.日期时间对象 2.创建日期时间对象 2.1.通过datetime.datetime.utcnow()创建 2.2.通过datetime.datetime.today()函数创建 2.3.通过datetime.datetime.now()创建 2.4.通过datetime.datetime()创建 2.5.查看创建的对象 2.6.查看datetime可以处理的最大的日期时间对象及最小的日期时间对象 3.日期事件对象的属性 4.日期时间对象转换为时间元组 5.将日期时间对象转化为公元历开始

  • Python标准库shutil用法实例详解

    本文实例讲述了Python标准库shutil用法.分享给大家供大家参考,具体如下: shutil模块提供了许多关于文件和文件集合的高级操作,特别提供了支持文件复制和删除的功能. 文件夹与文件操作 copyfileobj(fsrc, fdst, length=16*1024): 将fsrc文件内容复制至fdst文件,length为fsrc每次读取的长度,用做缓冲区大小 fsrc: 源文件 fdst: 复制至fdst文件 length: 缓冲区大小,即fsrc每次读取的长度 import shuti

  • python 标准库原理与用法详解之os.path篇

    os中的path 查看源码会看到,在os.py中有这样几行 if 'posix' in _names: name = 'posix' linesep = '\n' from posix import * #省略若干代码 elif 'nt' in _names: from nt import * try: from nt import _exit __all__.append('_exit') except ImportError: pass import ntpath as path #...

  • python标准库OS模块详解

    python标准库OS模块简介 os就是"operating system"的缩写,顾名思义,os模块提供的就是各种 Python 程序与操作系统进行交互的接口.通过使用os模块,一方面可以方便地与操作系统进行交互,另一方面页可以极大增强代码的可移植性.如果该模块中相关功能出错,会抛出OSError异常或其子类异常. 注意 如果是读写文件的话,建议使用内置函数open(): 如果是路径相关的操作,建议使用os的子模块os.path: 如果要逐行读取多个文件,建议使用fileinput模

  • Python标准库re的使用举例(正则化匹配)

    目录 常用正则表达式 一,必备知识 1.修饰符(flag) 2.匹配模式 3. r的作用 4. 贪婪与非贪婪—通配符后面加? 5.转义匹配的使用 6. group(num = 0)和groups( ) 二,函数应用 1. re.compile(pattern,flags=0)—自定义匹配模板 2. re.match(pattern,string,flags=0)—从第一个字符开始匹配 3. re.search(pattern,string,flags=0)—不固定开始,但是只匹配第一个 4. r

  • Python标准库之sqlite3使用实例

    Python自带一个轻量级的关系型数据库SQLite.这一数据库使用SQL语言.SQLite作为后端数据库,可以搭配Python建网站,或者制作有数据存储需求的工具.SQLite还在其它领域有广泛的应用,比如HTML5和移动端.Python标准库中的sqlite3提供该数据库的接口. 我将创建一个简单的关系型数据库,为一个书店存储书的分类和价格.数据库中包含两个表:category用于记录分类,book用于记录某个书的信息.一本书归属于某一个分类,因此book有一个外键(foreign key)

随机推荐