我认为静态类型似乎被吹捧过高了。

尽管如此,mypy极低的侵入性能带来许多好处。关于如何在现有的Python项目中添加类型,以下是我的一些想法,大致按重要性排序。

首先确保mypy成功运行 

Mypy上手时两个很常见的问题有:

1.Mypy没有作为构建的一部分运行

2.mypy 虽然正在运行,但它没有找到任何源文件或只找到了一部分源文件

Mypy的“默认允许”特性使两者都极易出现。无论出现哪种情况,都会非常痛苦,因为最后人们应用的类型,其实并未被检查,因而问题会慢慢暴露出来,让人非常困惑。

要手动添加类型的地方

Mypy 可进行类型推断,即通过检查有关值的代码,基于上下文区分值的类型。但事实上,由于mypy“渐进式推断”的特点,(如果不确定,它将推断成Any类型),比起像Haskell等其他推断性的语言,在python中更需要手动提供类型。

我目前的想法是:你应该力求为所有函数实参和返回值1 (以及其他任何mypy需要帮助的地方)提供类型。

一般来说,变量不需要应用类型,尽管这可能有助于使你的代码更清晰,或者帮助处理你不理解的类型错误。

Opitional会被频繁使用

在实践中最重要的一种类型就是 Optional. Optional 被用于可为空的值. 举个例子:

大量的代码会使用Optional配合其他类型使用。有了Optional,mypy就能够检查空值,无论该值被用在哪里。

Optional是一种简单的类型但它却能够查出大量的缺陷,它可能是整个类型检查体系中最好的部分。

考虑是否要包含你的测试

关于是否在类型检查中包含测试(即也对测试进行类型检查),我不知道有没有统一答案。有些项目确实能在 tests/  目录上运行mypy,有些则不能。

在类型检查2中包含测试的主要优点是:你能迅速发现应用类型和预期用法不一致,或mypy推断的类型与预期用法不符。

这在新的或没有太多类型应用的代码库中尤其有用。另外,测试也能用来改善IDE的 tab-completion

然而不利的一面就是,通常出于Mock(模拟)和Fake(伪造)的目的,测试有时会对你的代码进行一些古怪操作,而且,某些测试模式通过类型检查器可能有点困难 这虽然不是什么大问题,但似乎有点浪费时间,并且会削弱类型的优势。

有选择性地使用第三方stub(存根)

一些库包含大量运行时的元编程技巧. 因而这些库通常不会提供太多类型信息。

在程序中心使用了这些库,却不能获取类型信息,这会十分恼人。一些库有第三方Stub文件,例如sqlalchemy有sqlalchemy-stubs ,它会提供一些有用但不完全的类型。

这些库并非都有有用的第三方Stub。在撰写本文时,我对boto的任何第三方Stub都不信服。最好的方法似乎是在调用AWS / Openstack API时学会与Any一起使用(但要用moto进行彻底测试)。

偶尔需要应急措施

你偶尔会遇到这样的情况:代码正确但mypy无法分辨。这里有几种解决方法:

第一种(可能是最好的方法)就是用typing.cast,它会告诉  mypy 你知道的比它知道的更多。这将在整个代码中保持类型检查,除非通知 mypy做一个特定的更正。

第二种选择就是值显式设置为Any。这将禁用检查该特定值。如果所讨论的值是没有简单类型的复杂对象,则可以使用此方法。

第三种就是使用 # type: ignore 语法. 如果问题不在于确定特定类型,而在于mypy误认为被破坏了的某些不变类型,用这个会很方便。

考虑加一个注释去解释原因

优先选择一些严格性选项

Mypy的 mypy.ini 文件允许进行广泛的配置。我还没见过哪个实操项目避免使用mypy配置的。以下是我的首选:

  • check_untyped_defs 使mypy尝试检查没有类型注释的函数内部。否则,对于类型注释级别较低的项目mypy很少检查。

  • no_implicit_optional 当你设定参数不是空值,但实际上它们可为空时,它将提示类型错误。

  • ignore_missing_imports  这个应谨慎使用,不要将其应用于整个mypy 中,否则会极大地增加由于错误导致重要代码检查失败的风险。用此配置选项来标记特定模块(或名称空间)即可。

一个可行的例子:

一旦你的项目深度耦合了mypy,我建议最好去查看mypy提供的所有其他严格选项。从低严格度级别开始,朝着高严格度级别推进是一个好的策略。

如何调试类型问题

对于难懂的类型问题,这里给出两个策略来调试。

第一种:你可以将类型应用于出错类型周围,如变量、函数参数、循环迭代变量等。这样有助于将错误从难懂的那行代码中移动到更容易看出问题的地方。

第二:使用魔法般的mypy内置函数。reveal_type(expr)能使 mypy 打印出给定表达式类型的意见;reveal_locals() 则会让mypy打印出范围内所有变量的类型. 这两者中,我用 reveal_locals()  更多一些。

抽象,具体,可变和不变

Mypy 有具体的类型,如 List 和 Dict;也有抽象的类型,如 Sequence 和Mapping

一些抽象的类型还分可变和不可变的版本,例如Set 和 MutableSet ,Mapping 和 MutableMapping

因此我们需要去选择应用哪种类型及何时应用。

我的朋友Oli Russell提出以下策略:

  1. 让参数类型尽可能抽象

  • 这样能使调用者尽可能自由地传递他们想要的

2.让返回类型(更)具体

  • 同样也是为了让调用者尽可能自由地使用返回值

以上与我的经验相符。如果你过于苛刻, 比如,你返回 Sequence 而不是 List, 那么别人之后还要去编辑你的返回类型才能达到他们的目的。

同样的, 太具体的参数类型也很麻烦。你会发现无法将自定义的类似dict的对象传递给函数。有的对象只定义了__getitem__,但它确实可以当成一个Dict来使用。

你可以查阅 collections.abc 的标准库文档,去找每种抽象类型中包含的方法。以下是最常用的:

  • Iterable

  • Sequence 和 MutableSequence

  • Mapping 和 MutableMapping

  • Set 和 MutableSet

另外还有其他考虑。也许你想要阻止调用者修改你要返回的东西 (可能是因为你正在内部使用它3)。在这种情况下,最好返回 Mapping 而不是Dict。

Typed dataclasses(类型化的数据类)

Python 3.7 引入了 dataclasses. 下面是一个可能的类定义,如果你的类主要包含数据 (而不是行为)。它能为你提供一个更简洁的语法。

类型系统也支持这样的类,来作为其值的类型。

但缺点就是,大量更改其数据表示形式的代码往往不是快速代码。如果一个不可变的 Mapping作用于程序中的大部分,那它会比有一系列中间数据类要更快。

TypedDict

在 mypy 的扩展库中还有一个typed dictionary  。自3.8版本起它在标准库里。

TypedDict 能让你通过类型系统来控制存在哪些键以及它们的键值是什么,这一点胜过平常用的Mapping[str, str]。

它能成为Typed dataclasses的一个有效替代方案,且通常速度更快。

泛型和类型变量

mypy包括对类型变量和泛型类型的支持. 似乎大多数人发现如List等使用起来简单自然,但当有新的类型被创建出来之后,代码往往倾向于将类型限制定义为基类。

但是不是所有的泛型都不符合规则 , 在各式各样的Python 代码中,泛型偶尔也有使用价值。

泛型允许变量的类型更灵活 ,但它会保持检查 ,举个例子:

另一个例子:

泛型能让你定义专门作用于某种类型变量的类。下面这个例子中,我们将D绑定到一个特定的抽象基类。

我相信接下来几年这个将被广泛使用

ABCs vs Protocols

有时,需要将抽象类型应用于具有各种具体选项的事物(此处在实践中和使用泛型类型有交叉)。有两种方法可以做到这一点。

第一种:通过下面的方法命名父基类,子类将从该基类继承。

这里的 feed_animal 标记为采用Animal (一种抽象基类)。

第一种方法称为名义子类型化,作为在面向对象的语言中使用抽象类型的传统方式,大多数人应该熟悉。

还有第二种(相对于Python)较新的方法,在该方法中,您无需命名父类,而是命名一个有你所需方法的“协议”。

这里的feed_animal 被标记使用 Carnivore (一种协议)。注意,我不必将任何animal类标记为成员:如果它们具有相同类型的eat_meat方法,则它们将自动作为Carnivore的一部分

协议是“开放的”,因此任何具有匹配eat_meat方法的类都将被算作成员。这种方法叫做结构子类型化。有许多 built in protocols(内置协议),例如Sized,SupportsBytes,Container等。

如果你能控制足够多的类层次结构,则可以选择名义子类型,但是如果不能,则必须使用结构子类型。

与泛型类型一样,这个最好谨慎使用。希望绝大多数情况下,具体类型就够用了,这样就不必在代码库中填充大量协议和抽象基类了。

最后的提示

一些人太过于执迷类型了,我目睹了大量Haskell社区的人一头扎进自己的创作中无法自拔,变得痴痴傻傻,实在有点看不下去。

要小心变成类型狂!不要忽略这样一个事实:类型检查是修正错误的一种辅助,而勿满足于类型检查本身。

另请参阅

  • Dropbox是最早将mypy应用于其代码库的公司之一。对此他们也写了经验,读来十分有趣。"The Tangle"在许多Python项目中出现过。

注释:

  1. 强制执行此操作的相关配置选项是disallow_untyped_defs,但对现有项目立即启用它通常只会贪多嚼不烂。

  2. 请注意,你必须在与主代码库相同的mypy中运行测试。将测试分开各自运行是得不到任何好处的。

  3. 正如我在本文提到过的,避免在任何地方创建新集合的一个很好的理由是速度。用不可变的抽象类型标记返回类型有助于对此进行静态分析。

英文原文:http://calpaterson.com/mypy-hints.html
译者:ᐛ

aop在项目中的实际运用_mypy在实际项目中的应用相关推荐

  1. R语言关联规则挖掘数据集预览、分析、筛选:项目数的分布形态(分位数、密度图)、itemFrequency函数统计每一项目在所有事务中出现的次数、最常发生的项目、数据筛选(交易的集合项目大于1)

    R语言关联规则挖掘数据集预览.分析.筛选:项目数的分布形态(分位数.密度图).itemFrequency函数统计每一项目在所有事务中出现的次数.最常发生的项目.数据筛选(交易的集合项目大于1) 目录

  2. SAP实施项目中采购员在非生产性采购申请审批流中的角色安排

    SAP实施项目中采购员在非生产性采购申请审批流中的角色安排 所谓非生产性物料,也叫间接物料,也有些企业称之为MRO物料.它主要包括而不限于如下物料:办公用品,劳保用品,备品备件,服务,固定资产等等.这 ...

  3. java web 默认页面配置文件_Tomcat中配置全局的错误页面(如404)+删除Tomcat中webapps目录下的自带项目,防止Tomcat默认文件泄露...

    进入tomcat目录中conf文件编辑web.xml,将下面代码复制到文件末尾,如下 400 /error.html 404 /404/404.html 500 /404/500.html 2.添加4 ...

  4. ideal中如何添加几个不同的项目在同一个idea页面显示(同一个窗口显示多个工程)...

    今天,我遇到了一个问题,就是同事给了我一些项目,我下载了之后,项目有点多,然后想把这些项目都放到一个里面,所以我就采取了添加module的方式进行添加,首先先看一下我们的四个项目, 我们就想实现在一个 ...

  5. VS2010项目生成时提示错误“在证书存储区中找不到清单签名证书”?

    VS2010项目生成时提示错误"在证书存储区中找不到清单签名证书"? 解决办法:在项目属性里签名栏中的那个ClickOnce签名清单不知道什么时候勾选上了,去掉就一切正常了.如图 ...

  6. 怎么改vue项目的标题_如何动态修改Vue项目中的页面title

    前言:在项目中,我们有时候需要修改Vue项目中的页面title. 方法有两种,①如果需要动态设置页面的title,可以直接使用document.title:②可以使用router的beforeEach ...

  7. java中如何限制输入非空_项目中的参数限制、非空、以及集合中的验证,你真的会做吗(Java)...

    背景: 在项目管理中,我们通常要对一个个参数做验证, 前端校验,后端加校验,这里为了保证校验的一致性,我们需要将我们的校验写的比较高效率一点,不至于满篇都是is null的进行参数验证,为了成为一个成 ...

  8. J.R.R.托尔金笔下的中土世界与《斗破苍穹》项目实践:从世界观解构入手场景设计

    本期真经阁的特邀讲师将通过对<魔戒>与<霍比特人>这两部影视作品进行分析,为读者解构如何通过视觉上的表现设计来展现托尔金笔下的中土世界.并将其中讲述的方法应用在<斗破苍穹 ...

  9. 在Myeclipse中没有部署jeesite项目,但是每次运行其他项目时,还是会加载jeesite项目...

    解决办法: 一.在以下路径中找到jeesite文件,并删除 1.Tomcat 7.0\conf\Catalina\localhost 2.Tomcat 7.0\webapps 3.Tomcat 7.0 ...

最新文章

  1. luogu P2053 [SCOI2007]修车(费用流提前计算)
  2. sqoop 数据迁移
  3. usaco Arithmetic Progressions
  4. 在64位Windows 7 激活BitDefender Internet Security 2010
  5. Nexus刷官方下载的映像_occam
  6. android listview 横向滚动,Android支持水平滚动的ListView控件
  7. c++11 string u8_深入理解C++11:C++11新
  8. Problem01 不死神兔
  9. Split Byte(文件分割助手) v2.4
  10. 在线教学视频的设计与实现
  11. win10系统日志显示不可用sid_win10系统电脑修改sid安全标识符的操作方法
  12. [CQOI2015]选数
  13. smart原则_人生工具:SWOT、PDCA、6W2H、SMART、WBS、时间管理、二八原则
  14. 学习java.awt
  15. 数学建模常用算法:人工鱼群算法(AFAS)求解二元函数最小值+限定x,y范围测试【java实现--详细注释+Matlab绘制小鱼游动过程】
  16. 360金融:“巨头”式的快与稳
  17. 综合布线系统工程中计算机插座的标识符号是,TD是综合布线系统工程中计算机插座的标识符号。...
  18. 心田花开小学一年级阅读试题
  19. 一文简述机构资本市场中的区块链
  20. Linux学习基础文章1:Linux一句话精彩问答

热门文章

  1. Javascript面向对象编程:构造函数的继承
  2. Chrome 控制台的console用法收集
  3. vscode的 jsonp 配置文件
  4. B2C和B2B之间有多大差距
  5. Java(Android)线程池
  6. Visual Studio 常用快捷键 (二)
  7. 3013-04-13 腾讯笔试
  8. Extjs checkbox 多删除
  9. WinForm立体饼状图实现(附源码示例) 之配餐系统的开发
  10. 可以编辑vga格式文件的软件-PowerCreator Media Studio