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),虽然在定义了函数注释(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:python新手代码userid
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]
还⽀持⽤户定义的泛型类型别名。实例:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论