python用于类型注解的库- typing

简介: python用于类型注解的库- typing

一、简介

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

自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]]
#裙Python学习交流,153708845

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()
    ...
相关文章
|
2月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
205 1
|
3月前
|
存储 人工智能 测试技术
如何使用LangChain的Python库结合DeepSeek进行多轮次对话?
本文介绍如何使用LangChain结合DeepSeek实现多轮对话,测开人员可借此自动生成测试用例,提升自动化测试效率。
511 125
如何使用LangChain的Python库结合DeepSeek进行多轮次对话?
|
3月前
|
监控 数据可视化 数据挖掘
Python Rich库使用指南:打造更美观的命令行应用
Rich库是Python的终端美化利器,支持彩色文本、智能表格、动态进度条和语法高亮,大幅提升命令行应用的可视化效果与用户体验。
256 0
|
3月前
|
IDE 开发工具 开发者
Python类型注解:提升代码可读性与健壮性
Python类型注解:提升代码可读性与健壮性
283 102
|
2月前
|
数据可视化 关系型数据库 MySQL
【可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
本文详解基于Python的电影TOP250数据可视化大屏开发全流程,涵盖爬虫、数据存储、分析及可视化。使用requests+BeautifulSoup爬取数据,pandas存入MySQL,pyecharts实现柱状图、饼图、词云图、散点图等多种图表,并通过Page组件拖拽布局组合成大屏,支持多种主题切换,附完整源码与视频讲解。
261 4
【可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
|
2月前
|
传感器 运维 前端开发
Python离群值检测实战:使用distfit库实现基于分布拟合的异常检测
本文解析异常(anomaly)与新颖性(novelty)检测的本质差异,结合distfit库演示基于概率密度拟合的单变量无监督异常检测方法,涵盖全局、上下文与集体离群值识别,助力构建高可解释性模型。
330 10
Python离群值检测实战:使用distfit库实现基于分布拟合的异常检测
|
4月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
277 18
|
4月前
|
安全 JavaScript Java
Python中None与NoneType的真相:从单例对象到类型系统的深度解析
本文通过10个真实场景,深入解析Python中表示“空值”的None与NoneType。从单例模式、函数返回值,到类型注解、性能优化,全面揭示None在语言设计与实际编程中的核心作用,帮助开发者正确高效地处理“无值”状态,写出更健壮、清晰的Python代码。
460 3
|
4月前
|
机器学习/深度学习 API 异构计算
JAX快速上手:从NumPy到GPU加速的Python高性能计算库入门教程
JAX是Google开发的高性能数值计算库,旨在解决NumPy在现代计算需求下的局限性。它不仅兼容NumPy的API,还引入了自动微分、GPU/TPU加速和即时编译(JIT)等关键功能,显著提升了计算效率。JAX适用于机器学习、科学模拟等需要大规模计算和梯度优化的场景,为Python在高性能计算领域开辟了新路径。
411 0
JAX快速上手:从NumPy到GPU加速的Python高性能计算库入门教程
|
4月前
|
缓存 数据可视化 Linux
Python文件/目录比较实战:排除特定类型的实用技巧
本文通过四个实战案例,详解如何使用Python比较目录差异并灵活排除特定文件,涵盖基础比较、大文件处理、跨平台适配与可视化报告生成,助力开发者高效完成目录同步与数据校验任务。
175 0

推荐镜像

更多