🇨🇳
99 的博客
  • 久韭
  • Python
    • 你真的“创建”了类吗?
    • MRO 三定律
    • 描述器学习指南
    • 描述器实现 property
    • 如何做一个好中介
    • PEP 阅读清单
    • Python 双刃剑
    • 类型标注
    • “类”的渐进式剖析
  • Network
    • IPv6
    • 大道至简
    • DNS 根域
    • RFC 阅读清单
    • 基础公共服务
      • DNS
      • NTP
    • MAC 地址厂商信息
    • 点对点的中介
  • Windows
    • Windows 使用小技巧
  • Linux
    • SSH 技高一筹
    • nftables 速成
    • Linux 30 周年
    • 新时代 Linux 命令
    • 树莓派实时性优化
    • Bash DEBUG 一行搞定
  • 佳作收藏
    • 美文
      • 短篇
        • 爱
        • 弟弟
        • 暗途
        • 噩梦
        • 想象
        • 底线
        • 疯人
        • 雪兔
        • 夺妻
        • 闹钟
        • 虐猫
        • 做起来
        • 陈小手
        • 道德极限
        • 符号控制
        • 与人为友
        • 消失的人
        • 时间旅行
        • 情债肉偿
        • 铁血恋爱
        • 太阳黑子
        • 蚂蚁人生
        • 风骚和魅力
        • 我只要一种
        • 不朽的失眠
        • 秋天的怀念
        • 午夜的汽笛
        • 多美的故事
        • 相信不相信
        • 清晨的变故
        • 真实的高贵
        • 神秘领奖人
        • 幸福的夜晚
        • 幸福的生日
        • 我有事找你
        • 我是个窃贼
        • 最担心的是你
        • 客厅里的爆炸
        • 假如是你的话
        • 愿你慢慢长大
        • 民意与伪民意
        • 婚姻中没有天堂
        • 一种深久的不安
        • 非走不可的弯路
        • 单调产生的快乐
        • 我只是讨厌屈服
        • 跳槽只为“软福利”
        • 你的阅读造就了你
        • 聪明人和傻子和奴才
        • 你永远有做不完的事
        • 精神病院里的年轻人
        • 一位短跑运动员的孤独
        • 那些你所不知道的大事
        • 穿过大半个中国去睡你
        • 我觉得自己会永远生猛下去
      • 长篇
        • 早餐
        • 波心
        • 老王
        • 翻浆
        • 小猫
        • 良娼
        • 自信
        • 黑羊
        • 猫人
        • 斜眼
        • 识人
        • 身价
        • 肯肯舞
        • 狗和猫
        • 热包子
        • 黄裙子
        • 老江湖
        • 逃脱术
        • 侯银匠
        • 太原诅咒
        • 死亡花朵
        • 罗马惊艳
        • 刺青时代
        • 好嘴杨巴
        • 扼杀胎儿
        • 纪念照片
        • 紫色人形
        • 天外飞石
        • 戒烟公司
        • 蔡二少爷
        • 女人的星球
        • 一千张糖纸
        • 爱情与投资
        • 赌徒的遗书
        • 飞越流水线
        • 跟踪狂入门
        • 一桩自杀案
        • 机舱里的钟声
        • 桌子还是桌子
        • 一小时的故事
        • 不存在的女友
        • 谁在编造历史
        • 父亲坐在黑暗中
        • 一个小小的建议
        • 彬彬有礼的强盗
        • 18 本画册的爱恋
        • 河流最蓝的地方
        • 好人总会有人疼
        • 偷听谈话的妙趣
        • 公主整夜不能睡
        • 他们那时多有趣
        • 布莱克·沃兹沃斯
        • 坐在路边鼓掌的人
        • 最伟大的科幻小说
        • 你丈夫是干什么的
        • 谁也看不见的阳台
        • 上午打瞌睡的女孩
    • 段子
      • 程序员段子
      • 二次元段子
      • 大老师片段
      • 金句
      • 喝痰
      • 数学家
      • 牛子
      • 男 ♂ 语
      • 甲方乙方
      • 新编故事四则
      • 没人知道大东为何在此舞蹈
  • 日常随记
    • 坑
    • 豆知识
    • 逻辑推断
    • 观影后记
    • LaMDA 有意识吗?
    • VS Code Vim 速记
    • QQ 空间表情符号
    • ZEN of DEBUG
    • 数据收集的途径
    • 呼叫转移设置方式
    • 空中浩劫 ACI 经典
    • 联机海难特色简述
    • 智能家居入门指南
    • 财政学笔记
    • 行为与实验经济学笔记
    • 电商管理学笔记
由 GitBook 提供支持
在本页
  • 序言
  • 引入类型标注的动机
  • 类型标注的理论基础
  • 语法详解
  • 变量
  • 函数
  • 方法
  • 高级特性
  • 泛型
  • TypeGuard
  • 参考
在GitHub上编辑
  1. Python

类型标注

上一页Python 双刃剑下一页“类”的渐进式剖析

最后更新于1年前

序言

编程语言界最常见到的动态类型语言莫过于 Javascript 和 Python,尽管二者在其设计哲学、应用领域方面有着巨大的差异,但随着它们用户基数的增长,都出现了一个相同的发展趋势——类型静态化。对于 Javascript 来说,Typescript 的出现就是最好的例子;而对于 Python 而言,由 PEP 484 在标准库中引入的 typing 模块也可见一斑。

好好的动态类型,为什么都要朝着静态类型发展呢?

一言以概之:“动态类型写着爽,维护升级火葬场”。

就以我们的主角 Python 而言,在其发展早期,它只不过是作为一个小小的工具语言,用以在大型项目中的边边角角做一些简单的辅助性处理。那时的 Python,一般也就是个两三千行撑死,莫说是复杂的类型结构,很可能连 class 语句都不会出现。在这种基本就是内置类型传来传去的情况下,动态类型让老练的开发者能够快速高效地完成简单的工作,一时之间受到了大家的追捧。

伴随着 Python 的流行,越来越多的项目采用 Python 作为主语言。而当一个项目拥有过万行的代码量以及完整而精巧的类型结构设计时,动态类型的弊端就开始显现。函数签名的自解释性匮乏,开发者不得不“面向文档编程”;错误的类型传递无法避免,隐藏的 BUG 开始积累;类型的含义逐步被淡化,精心构造的类型结构失去价值……这时候,大家又开始怀念起了静态类型的好,写代码时多跳几个 type error 总好过 DEBUG 时抓心挠肺。

在这段时期,一个名叫 mypy 的第三方包的兴起令 Python 核心开发者意识到了真正的问题所在:码农们对于静态类型的偏好其实源于对类型检查器的依赖。mypy 就是一个静态类型检查器,通过解析 Python 代码及其中包含的特定的注释,在 Python 代码实际运行前进行静态的类型检查。而这是一个兼容性非常棒的解决方案:类型声明的内容均包含在注释中,不会对原有的代码含义产生任何影响;类型检查独立工作在代码运行前,不会对代码的实际运行产生任何影响。2014 年 9 月 29 日,Python 之父 Guido van Rossum、mypy 之父 Jukka Lehtosalo 及 Python 核心开发者 Łukasz Langa 联手发布了 ,在 Python 标准库中引入了全新的 typing 模块,提供对类型标注的官方支持。

时至今日,类型标注已经成为 Python 的核心语言特性之一,但相关的中文资料多是只言片语,缺乏系统性的介绍,故开此篇以作补充。

本文内容基于 Python 3.10.2

引入类型标注的动机

Python 项目中需要引入类型检查以维持项目的可持续发展,但是不应为此改变 Python 自诞生以来的动态语言特征。而社区中已有被广泛接受的解决方案:在源代码中使用注释补充变量信息,检查器在代码运行前进行(静态)类型检查。

静态类型检查的高度实用性与优雅性赢得了整个 Python 社区的青睐,故而 Python 核心开发者们决定在核心语法特性中引入官方支持。

但需要强调的是,类型标注 与静态类型语言中的 变量声明 不尽相同,且这不会动摇 Python 一直以来作为动态类型语言的本质与设计哲学。

Python 将仍然是一种动态类型的语言,作者不希望强制类型提示,即使按照惯例也是如此。

——PEP 484

类型注释不应与静态类型语言中的变量声明相混淆。注释语法的目标是提供一种简单的方法来为第三方工具指定结构化类型元数据。

——PEP 526

类型标注的理论基础

Python 所采用的类型标注运用了 渐进式类型(Gradual Typing) 理论。

常见编程语言对类型检查的实现,存在两个派别——动态类型检查与静态类型检查。

动态类型检查的代表性语言包括 Perl、Python、Javascript、Ruby 和 PHP,“动态”的含义是类型检查发生在程序代码实际执行期间,即对内存中已经存在的数据进行操作时才对其类型进行检查。

静态类型检查的代表性语言包括 Java、C#、C 和 C++,“静态”的含义是类型检查发生在程序代码实际执行之前,此时内存中并没有实际生成对应数据。

这两派的支持者间关于谁优谁劣的争论已经持续了数十年,至今仍为得出一致的结论。

个人认为这场争论的核心是围绕 开发效率、运行效率、维护效率 的取舍:动态类型检查以损失运行效率和维护效率为代价,大大提升了开发效率;静态类型检查以较大的开发效率损失换来了更高的运行效率和维护效率。尽管偏好相异,但二者都对开发效率做出了相对极端的取舍。

区分类与类型

类型继承与子类型

语法详解

在介绍语法之前,我们需要先区分两个易混淆的概念—— 类 和 类型。

类型是用于描述对象特征的抽象概念,拥有一系列相同的功能、值的对象可以归为一个类型。类型是一个静态的、逻辑性的概念。

“类型标注”这个名称已经明确表明了其标注的内容是类型而非类,所以一定要注意,应将类型标注中使用的名称作为类型而非简单的类来看待。

变量

函数

方法

高级特性

泛型

TypeGuard

参考

与 Walid Taha 联手在 2006 年提出了渐进式类型理论,该理论结合了动态类型检查和静态类型检查

在 Python 中,类是由 class 语句定义的对象工厂,并由 type(obj) 内置函数返回。类是一个动态的、运行时的概念。具体可参见作者另一篇博文

PEP 484 -- Type Hints
Jeremy Siek
你真的“创建”了类吗?
typing --- 类型提示支持
PEP 483 -- The Theory of Type Hints
PEP 484 -- Type Hints
PEP 526 -- Syntax for Variable Annotations
PEP 544 -- Protocols: Structural subtyping (static duck typing)
PEP 585 -- Type Hinting Generics In Standard Collections
PEP 586 -- Literal Types
PEP 589 -- TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys
PEP 591 -- Adding a final qualifier to typing
PEP 593 -- Flexible function and variable annotations
PEP 604 -- Allow writing union types as X | Y
PEP 612 -- Parameter Specification Variables
PEP 613 -- Explicit Type Aliases
PEP 647 -- User-Defined Type Guards
What is Gradual Typing