Python3标准库漫游之Typing.overload
Python3 >= 3.5
Python3.5开始Python把Typing作为标准库引入,低版本可以使用独立的Typing包
问题来源于一个QQ群友的提问,顺着问题我看了下Typing中overload的使用。
Python3中增加了Function Annotation的功能,翻译过来就是函数(方法)注解,具体用法就是:
这么定义函数,可以达到静态类型的效果。如果你尝试使用foo(2) 传入一个int类型的话就会报错:
但这是运行时的错误,也就是代码在执行时才会发现问题。最好是我们能在写完代码时就发现是否存在问题。因此就有了类型检查工具,比如mypy这样的工具,还有很多IDE也集成了这样的检查工具。
如果用mypy检查上面的代码就会得到如下提示:
有了Annotation的Python3显然是对Python大工程开发能力的增强,动态语言最大的弊端就是太灵活,当然这也是优点。不过对于有经验的开发来说,避免去修改函数的输入和输出是重要的设计思想。对于以往的参数,因为没有类型信息,所以我们需要在函数内部去做各种判断,来保证数据类型符合预期。
overload存在的必要
然后我们再来看overload。
overload翻译过来是“重载”的意思,Java中有这样的两个概念,重写(override)和重载(overload)。重写其实是在保证输入和输出不变的情况下重写实现逻辑。而重载则是允许修改输入和输出,即同一个方法名可以支持多种类型的输入和输出。
上面介绍了annotation能够声明类型,这样在执行时能发现错误,也能够在静态检查阶段发现错误。但是如果函数的参数确实需要多种类型呢?不能因为静态类型的声明而导致动态易用性的损失吧。
因此有两种方案:
一、使用typing.TypeVar
二、使用typing.overload
先说第一种,对于固定数量参数的方法而言,同一个参数如果打算接受多种类型,可以这么用,比方说参数可以是:int, float, str:
这种方案更类似于静态语言中的interface的概念,定义一个通用的父类,这样的话,你可以传递子类型过去。
再来看第二种的解决方案,也就是重载的方式。但是跟静态语言中还是很有差别。
通过定义多个同名函数,上面的同名函数需要通过overload装饰器装饰。可以看到被装饰的函数的输入类型和输出类型都可以更改。但是,最后的实现方法一定要通用,也就是没有类型注解。
这么用的作用是什么呢?文档中有一句话很重要:
“The @overload-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-@overload-decorated definition, while the latter is used at runtime but should be ignored by a type checker. ”
翻译过来就是, 被overload装饰的函数仅仅是为了受益于类型检查工具,因为它们会被没有overload装饰的函数定义覆盖,尽管未被装饰的函数是用于运行时的,但是会被类型检查工具忽略。
所以,看到这应该明白了,overload仅仅是给检查工具用的。但如果静态类型检查变成工程的一部分的话,这也会避免很多问题,在写代码时也会比心里有底。
不过上面的代码仅仅用来说明typing.overload的作用。
总结
需要提醒的是,Python3.5.1版本的overload是不对外使用的,如果你在这个版本下尝试上面的代码应该会报错: Overloading is only supported in library stubs。
Annotation确实是个不错的特性,但是因此也需要引入更多的配套来保证整个环境的一致。因此就有了剩下的东西。可能后面还会有其他的东西。
另外谨慎声明:本篇文章仅仅是作者在看文档和部分代码是的一些想法,仅供参考。
最后讲一个段子:某创业公司司招了个比较水的程序员,在招不到人的情况下,为了保证项目进度,于是又招了项目经理。因为当程序员较水,搞不定服务器上的配置,于是又招了个运维工程师,...
——段子来源:陈浩(coolshell)在<代码时间>的访谈。
参考
https://docs.python.org/3/library/typing.html
https://github.com/mirumee/prices