利用Type Hint提升Python程序开发效率
pojb8446
8年前
<p>Type Hint(或者叫做PEP-484)提供了一种针对Python程序的类型标注标准。</p> <p>为什么使用Type Hint?对于动态语言而言,常常出现的情况是当你写了一段代码后,隔段时间你可能忘记这个方法的原型是什么样子的了,你也不清楚具体应该传入什么类型的参数,这样往往需要你去阅读代码才能定义每个类型具体是什么。或者当你使用一个文档并不是特别完全的第三方库,你不知道这个库应该如何使用,这都会很痛苦。</p> <p>现在,借助Type Hint,你可以实现:</p> <ul> <li>实现类型检查,防止运行时出现的类型不符合情况。</li> <li>作为文档附加属性,方便开发者调用时传入传出的参数类型。</li> <li>提升IDE的检查机制,在智能提示时更快给出提示和类型检查结果。</li> </ul> <p>实现这个过程中,你需要使用<code>Python 3.5+</code>中提供的新模块<a href="/misc/goto?guid=4959675783758088446"><code>typing</code></a>。值得注意的是,这个改动并不会影响程序运行,仅仅是为了方便类型检查器实现的。</p> <h2>Type Hint类型检查器</h2> <p>目前,比如<code>JetBrains</code>家的<code>PyCharm</code>已经支持Type Hint语法检查功能,如果你使用了这个IDE,可以通过IDE功能进行实现。如果你像我一样,使用了SublimeText编辑器,那么第三方工具<a href="/misc/goto?guid=4959675783846468116"><code>mypy</code></a>可以帮助到你。<code>AnacondaST3</code>最近要发布的2.0版本也内置了<code>mypy</code>功能的支持,具体的进度可以看一下<a href="/misc/goto?guid=4959675783929763632">这个issue</a>。一些其它的Python工具(比如<a href="/misc/goto?guid=4959675784010982698">代码提示工具jedi 0.10+</a>)也支持了Type Hint功能。</p> <h2>从简单的例子开始</h2> <p>从简单的例子开始,我们先从一个简单的程序开始,运行环境为<code>Python 3.5.2</code>,使用<code>mypy</code>工具进行检查。</p> <p>首先通过<code>pip install mypy-lang</code>命令安装<code>mypy</code>工具。注意是<code>mypy-lang</code>,之所以是这样,是因为在<code>pypi</code>里<code>mypy</code>这个名字已经被占用掉了。</p> <p>接下来,通过<code>mypy</code>检查下面这个文件</p> <pre> <code class="language-python"># fib.py from typing import Iterator def fib(n: int) -> Iterator[int]: a, b = 0, 1 while a < n: yield a a, b = b, a + b i = fib(3.2) print(next(i)) print(next(i)) </code></pre> <p>在命令行中执行命令<code>mypy fib.py</code>,获取返回结果:</p> <pre> <code class="language-python">➜ mypy fib.py fib.py:11: error: Argument 1 to "fib" has incompatible type "float"; expected "int" </code></pre> <p>但是在实际的应用过程中,这个功能在Python里是可以正常运行的:</p> <pre> <code class="language-python">➜ mypy python fib.py 0 1 </code></pre> <p>可以看到,mypy工具提示了我们的代码中存在一处类型不匹配的问题,但是如果不进行检查,代码有可能执行出不可预知的结果。</p> <p>在这个例子里面,我们使用了两种类型,一种是Python基础数据类型,比如<code>str</code>、<code>int</code>等等,这些类型数据是可以直接使用的;另外一种是来自于<code>typing</code>中引入的<code>Iterator</code>,用来表示迭代器类型。另外一个值得注意的是,<code>typing</code>中部分类型也会随时添加,一般我们以演示版本为准。</p> <h2>从简单到复杂,类型组合怎么办?</h2> <p>实际上,在我们使用过程中还有可能传递一些更加复杂的参数类型,比如list类型,tuple类型等等,这类型的数据如何声明呢?我们可以先看一个例子:</p> <pre> <code class="language-python">def foo(strings, string_list, count, total): </code></pre> <p>这个函数的参数我们从字面可以看出来分别是<code>str</code>,元素为<code>str</code>的<code>list</code>类型和两个整数参数。我们假定一个返回值为<code>((int, int), str)</code>,那么这个类型检查可以这样定义:</p> <pre> <code class="language-python">from typing import List, Tuple Result = Tuple[Tuple[int, int], str] def foo(strings: str, lines: List[str], line_number: int, total_lines: int) -> Result: </code></pre> <p>其它的一些类型提示、协程等等的支持都可以在官方的<a href="/misc/goto?guid=4959675783758088446"><code>typing</code>模块文档</a>中进行查看。</p> <h2>关于生产的一些闲扯</h2> <p>我们现在也在进行一些<code>mypy</code>工具在生产环境中的具体使用测试,但是我们也发现了一些存在的问题,比如<code>Python</code>本身的动态语言特性给类型标注就带来了一些麻烦。另外,变量复用导致的类型变换有可能会提示采用新的变量实现。这对于一个已经存在的线上项目来说相对成本较高,我们后续也会在一些新项目中采用这种方式。另外<code>mypy</code>还是一个比较新的项目,本身是拥有一些bug。另外一个是在某些<code>mypy</code>的非类型错误提示其实非常的模糊,导致很多错误有时需要进行人工排查。</p> <p>不管怎样,即便在<code>mypy</code>存在一些缺陷,但是仍旧是未来非常有潜力的工具,提前了解和应用也能有效的提升程序的强壮性。</p> <p> </p> <p>来自:https://ipfans.github.io/2016/07/type-hint-improve-python-programming/</p> <p> </p>