写在篇前

  typingpython3.5中开始新增的专用于类型注解(type hints)的模块,为python程序提供静态类型检查,如下面的greeting函数规定了参数name的类型是str,返回值的类型也是str

def greeting(name: str) -> str:return 'Hello ' + name

  在实践中,该模块常用的类型有 Any, Union, Tuple, Callable, TypeVar,Optional和Generic等,本篇博客主要依据官方文档以及日常使用经验来探讨一下typing模块的使用方法以及经验。

注意事项:typing模块虽然已经正式加入到了标准库中,但是如果核心开发者认为有必要的话,api也可能会发生改变,即不保证向后兼容性

Type aliases

  简单的类型注解及其形式如开篇例子所示,那么除了默认的int、str等简单类型,就可以通过typing模块来实现注解。首先,我们可以通过给类型赋予别名,简化类型注释,如下例中的VectorList[float]是等价的。

from typing import List
Vector = List[float]def scale(scalar: float, vector: Vector) -> Vector:return [scalar * num for num in vector]

  上面的例子,似乎不能很好的体现类型注释别名的优势,官网还给了另外一个例子,非常生动形象:

from typing import Dict, Tuple, SequenceConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]def broadcast_message(message: str, servers: Sequence[Server]) -> None:passdef broadcast_message2(message: str,servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:pass

  毫无疑问,函数broadcast_message2的注解明显比broadcast_message更加简洁清晰。

NewType

  可以使用NewType来创建一个用户自定义类型,如:

from typing import NewTypeUserId = NewType("UserId", int)
def get_user_name(user_id: UserId) -> str:pass# 可以通过类型检查
user_a = get_user_name(UserId(42351))
# 不能够通过类型检查
user_b = get_user_name(-1)

  NewType的实现方式很简单,在运行时,NewType(name, tp)返回一个函数,这个函数返回其原本的值。静态类型检查器会将新类型看作是原始类型的一个子类。

# NewType实现代码
def NewType(name, tp):def new_type(x):return xnew_type.__name__ = namenew_type.__supertype__ = tpreturn new_type

  因为NewType被看做原始类型的子类,因此在新类型上你可以进行原始类型允许的操作,且结果的类型是原始类型,看起来是不是很神奇,甚至有点绕!其实关键还是要理解其原理,举两个例子:

  • 实例1

    >>> user_id_1 = UserId(23)
    >>> user_id_2 = UserId(46)
    >>> user_id_1 + user_id_2
    69
    
  • 实例2

    # 请和NewType第一个例子对比
    def get_num(num: int) -> int:return num# 可以通过类型检查
    get_num(1)user_id: UserId = UserId(23)
    # 可以通过类型检查
    get_num(user_id)
    

  

  请注意,这些检查仅会被静态类型检查程序强制执行。在运行时,Derived = NewType('Derived',Base)Derived 一个函数,该函数立即返回传递给它的任何参数。这意味着表达式 Derived(some_value) 不会创建一个新的类或引入任何超出常规函数调用的开销。更确切地说,表达式 some_value is Derived(some_value) 在运行时总是为真。这也意味着无法创建 Derived 的子类型,因为它是运行时的标识函数,而不是实际的类型。

Callable

  回调函数可以使用类似Callable[[Arg1Type, Arg2Type],ReturnType]的类型注释,这个比较简单,例子如下,如果只指定回调函数的返回值类型,则可以使用Callable[..., ReturnType]的形式:

from typing import Callabledef async_query(on_success: Callable[[int], None],on_error: Callable[[int, Exception], None]) -> None:pass

Generics

  由于无法以通用的方式静态推断有关保存在容器(list set tuple)中对象的类型信息,因此抽象类被用来拓展表示容器中的元素。如下面子里中,使用基类Employee来扩展其可能得子类如 Sub1_EmployeeSub2_Employee等。但是其局限性明显,所以我们需要引入泛型(generics)。

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

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

from typing import Sequence, TypeVarT = TypeVar('T')      # Can be anything
A = TypeVar('A', str, bytes)  # Must be str or bytesdef first(l: Sequence[T]) -> T:   # Generic functionreturn l[0]

User-defined generic types

  可以将用户字定义的类定义为泛型类:

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

  Generic[T] 作为基类定义了类 LoggedVar 采用单个类型参数 T。这也使得 T 作为类体内的一个类型有效。通过Generic基类使用元类(metaclass)定义__getitem__()使得LoggedVar[t]是有效类型:

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

  泛型类型可以有任意数量的类型变量,并且类型变量可能会受到限制:

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

Any Type

  Any是一种特殊的类型,静态类型检查器视Any与任何类型兼容,任何类型与Any兼容。

def foo(item: Any) -> int:item.bar()

写在篇后

  在实际使用中, Any, Union, Tuple, List, Sequence, Mapping, Callable, TypeVar,Optional, Generic等的使用频率比较高,其中Union、Optional、Sequence、Mapping非常有用,注意掌握。

  • Union

    即并集,所以Union[X, Y] 意思是要么X类型、要么Y类型

  • Optional

    Optional[X]Union[X, None],即它默认允许None类型

  • Sequence

    即序列,需要注意的是,List一般用来标注返回值;Sequence、Iterable用来标注参数类型

  • Mapping

    即字典,需要注意的是,Dict一般用来标注返回值;Mapping用来标注参数类型

  贴一段类型标注的实例代码,是不是让人一目了然,不需要看具体代码逻辑就知道参数类型以及如何调用呢?

def __init__(self,X: Optional[Union[np.ndarray, sparse.spmatrix, pd.DataFrame]] = None,obs: Optional[Union[pd.DataFrame, Mapping[str, Iterable[Any]]]] = None,var: Optional[Union[pd.DataFrame, Mapping[str, Iterable[Any]]]] = None,uns: Optional[Mapping[str, Any]] = None,obsm: Optional[Union[np.ndarray, Mapping[str, Sequence[Any]]]] = None,varm: Optional[Union[np.ndarray, Mapping[str, Sequence[Any]]]] = None,layers: Optional[Mapping[str, Union[np.ndarray, sparse.spmatrix]]] = None,raw: Optional[Raw] = None,dtype: Union[np.dtype, str] = 'float32',shape: Optional[Tuple[int, int]] = None,filename: Optional[PathLike] = None,filemode: Optional[str] = None,asview: bool = False,*, oidx: Index = None, vidx: Index = None):

  

  类型标注可以使程序的维护性、使用性更高,这一点非常重要;另外,许多IDE配合类型标注可以增强智能提示功能,加快编码速度,提高效率,我们何乐而不为呢?

python Typing模块-类型注解相关推荐

  1. [转]关于Python里的类型注解

    我们知道 Python 是一种动态语言,在声明一个变量时我们不需要显式地声明它的类型,例如下面的例子: a = 2print('1 + a =', 1 + a) print('1 + a =', 1 ...

  2. python typing optional_python类型检测最终指南--Typing模块的使用

    正文共:30429 字 预计阅读时间:76分钟 原文链接:https://realpython.com/python-type-checking/ 作者:Geir Arne Hjelle 译者:陈祥安 ...

  3. Python Type Hint类型注解

    原文地址:https://realpython.com/python-type-checking/ 在本指南中,你将了解Python类型检查.传统上,Python解释器以灵活但隐式的方式处理类型.Py ...

  4. python中函数type可以测试对象类型_python类型检测最终指南--Typing模块的使用

    正文共:30429 字 预计阅读时间:76分钟 原文链接:https://realpython.com/python-type-checking/ 作者:Geir Arne Hjelle 译者:陈祥安 ...

  5. python 类型注解 list_Python 类型注解

    说明 Python 教程正在编写中,欢迎大家加微信 sinbam 提供意见.建议.纠错.催更. 简单说,Python 类型注解功能可以让我们的代码更加易读,从而达到编写更加健壮的代码目标.类型注解又叫 ...

  6. 《流畅的Python第二版》读书笔记——函数中的类型注解

    引言 这是<流畅的Python第二版>抢先版的读书笔记.Python版本暂时用的是python3.10.为了使开发更简单.快捷,本文使用了JupyterLab. 本章关注于Python在函 ...

  7. ​Python 3 新特性:类型注解——类似注释吧,反正解释器又不做校验

    ​Python 3 新特性:类型注解 Crossin ​ 上海交通大学 计算机应用技术硕士 95 人赞同了该文章 前几天有同学问到,这个写法是什么意思: def add(x:int, y:int) - ...

  8. 【Python教程】typing模块的作用-提高代码健壮性

    typing模块的作用: 类型检查,防止运行时出现参数和返回值类型不符合. 作为开发文档附加说明,方便使用者调用时传入和返回参数类型. 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒. ...

  9. Python 3 新特性:类型注解

    Python 3 新特性:类型注解 之前也看到这种写法,有人疑惑这个写法是什么意思: def add(x:int, y:int) -> int:return x + y 我们知道 Python ...

最新文章

  1. Android Binder设计与实现 – 设计篇
  2. 名校 Stanford
  3. HttpUrlConnection get和post简单实现(疑惑解决)
  4. Linux——进程系列知识详述(操作系统、PCB进程控制块、查看进程状态等)
  5. 微软2021校园招聘正式启动
  6. VSX-5 VSXMusic 编码听音乐
  7. 时间轮算法解析(Netty HashedWheelTimer源码解读)
  8. 状态压缩 + 暴力 HDOJ 4770 Lights Against Dudely
  9. wpf notifyIcon
  10. 【numpy】numpy.zeros()函数
  11. matlab gui 图像增强,基于MATLAB GUI的图像增强技术的实现
  12. zabbix登陆拒绝报没有权限
  13. gitblit如何迁移入gitlab合并迁移_最新gitlab备份迁移方案
  14. 中小企业网络推广方案
  15. flutter 截图 截长图 滚动截图 保存到手机
  16. 2021-2022 ACM-ICPC Brazil Subregional Programming Contest C Creating Multiples
  17. SDUT最短路径问题 1867————最短路
  18. 网易云催生云计算更多可能性
  19. PAT 乙级 1040 有几个PAT (25分)
  20. SCI论文写作的学习与总结

热门文章

  1. Netty之自定义编码器MessageToMessageEncoder类
  2. 简洁易懂:c:out标签详解
  3. 一条SQL更新语句是如何执行的?
  4. 详细图文演示——排除启动类故障以及Linux操作系统引导、运行级别和优化启动等相关知识
  5. python 写txt 换行_python中写入txt文件需要换行,以及\r 和\n
  6. jsp页面实现打印 .
  7. Android下检测网络连接 3G WIFI
  8. 公平锁非公平锁的实际使用_3. redisson源码公平锁之队列重排序
  9. python的字符串_百度资讯搜索_python的字符串
  10. c++ 使用nacos_Nacos配置的多环境管理