PEP 3117 – 后缀类型声明
- 作者:
- Georg Brandl <georg at python.org>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2007年4月1日
- Python 版本:
- 3.0
- 发布历史:
摘要
本 PEP 提议为 Python 添加后缀类型声明语法。它还指定了一个新的 typedef 语句,用于创建类型和声明符之间的新映射。
它的接受将极大地增强 Python 用户体验,并消除阻碍其他编程语言用户转向 Python 的一个缺陷。
基本原理
Python 长期以来一直缺乏显式的类型声明。作为该语言偏离其“Python 之禅”的少数方面之一,这个缺陷引发了许多关于 Python 异端和 PSU 成员之间的讨论(例如,请参阅 [EX1]、[EX2] 或 [EX3]),并且还使得大规模企业成功变得不太可能。
然而,如果人们想要结束这种痛苦,就必须找到一种体面的 Pythonic 语法。在几乎所有拥有类型声明的语言中,类型声明都缺乏这种特质:它们冗长,通常需要多个单词来表示一个类型,或者难以理解(例如,某种语言使用完全不相关的 [1] 形容词,如 dim 来声明类型)。
因此,本 PEP 将类型声明的举措与另一项大胆的举措相结合,这将再次证明 Python 不仅是面向未来的,而且拥抱未来:将 Unicode 字符引入为源代码的组成部分。
Unicode 使得能够用更少的字符表达更多内容,这与 Python 之禅(“可读性很重要。”)一致。此外,它消除了对单独类型声明语句的需要,最重要的是,它使 Python 能够与 Perl 6 相媲美,Perl 6 已经为其运算符使用了 Unicode。 [2]
规范
当类型声明模式运行时,语法会发生更改,以便每个 NAME 都必须包含两部分:一个名称和一个类型声明符,而类型声明符正好是一个 Unicode 字符。
声明符唯一地指定了名称的类型,如果它出现在表达式的左侧,则强制执行此类型:如果返回的类型与声明的类型不匹配,则会引发 InquisitionError 异常。 [3]
此外,函数调用结果类型也必须指定。如果调用的结果不具有声明的类型,则会引发 InquisitionError。注意:函数对象的声明符不应与结果的声明符混淆(请参阅下面的示例)。
仅读取而不赋值的名称后面的类型声明符并非严格必需,但仍会强制执行(请参阅 Python 之禅:“显式优于隐式。”)。
类型和声明符之间的映射不是静态的。它可以完全由程序员自定义,但为了方便起见,为一些内置类型提供了一些预定义映射。
| 类型 | 声明符 |
|---|---|
对象 |
� (替换字符) |
int |
ℕ (双线大写 N) |
浮点数 |
℮ (估计符号) |
布尔值 |
✓ (勾号) |
复数 |
ℂ (双线大写 C) |
str |
✎ (右下铅笔) |
Unicode |
✒ (黑色笔尖) |
元组 |
⒯ (带括号的小写字母 t) |
列表 |
♨ (温泉) |
字典 |
⧟ (双向多重映射) |
集合 |
∅ (空集)(注意:这也可以用于满集) |
冻结集合 |
☃ (雪人) |
日期时间 |
⌚ (手表) |
函数 |
ƛ (带斜杠的拉丁小写字母 lambda) |
生成器 |
⚛ (原子符号) |
异常 |
⌁ (电箭头) |
None 类型的声明符是零宽度空格。
这些字符应该对每个程序员来说都很明显、易于记住和输入。
Unicode 替换单元
鉴于即使在我们现代、全球化的世界中,仍然有一些老派的叛逆者无法或不愿在源代码中使用 Unicode,并且 Python 是一种宽容的语言,因此为那些人提供了一个后备方案。
与单个 Unicode 字符相反,他们可以输入 name${UNICODE DECLARATOR OF NAME}$。例如,这两个函数定义是等效的。
def fooƛ(xℂ):
return None
和
def foo${LATIN SMALL LETTER LAMBDA WITH STROKE}$(x${DOUBLE-STRUCK CAPITAL C}$):
return None${ZERO WIDTH NO-BREAK SPACE}$
这仍然易于阅读,并且使 ASCII 信徒能够使用类型注解的 Python 的全部功能。
typedef 语句
类型和声明符之间的映射可以使用此新语句进行扩展。
语法如下:
typedef_stmt ::= "typedef" expr DECLARATOR
其中 expr 解析为类型对象。为了方便起见,typedef 语句还可以与新类的 class 语句混合使用,如下所示:
typedef class Foo☺(object�):
pass
示例
这是标准的 os.path.normpath 函数,已转换为类型声明语法。
def normpathƛ(path✎)✎:
"""Normalize path, eliminating double slashes, etc."""
if path✎ == '':
return '.'
initial_slashes✓ = path✎.startswithƛ('/')✓
# POSIX allows one or two initial slashes, but treats three or more
# as single slash.
if (initial_slashes✓ and
path✎.startswithƛ('//')✓ and not path✎.startswithƛ('///')✓)✓:
initial_slashesℕ = 2
comps♨ = path✎.splitƛ('/')♨
new_comps♨ = []♨
for comp✎ in comps♨:
if comp✎ in ('', '.')⒯:
continue
if (comp✎ != '..' or (not initial_slashesℕ and not new_comps♨)✓ or
(new_comps♨ and new_comps♨[-1]✎ == '..')✓)✓:
new_comps♨.appendƛ(comp✎)
elif new_comps♨:
new_comps♨.popƛ()✎
comps♨ = new_comps♨
path✎ = '/'.join(comps♨)✎
if initial_slashesℕ:
path✎ = '/'*initial_slashesℕ + path✎
return path✎ or '.'
正如您清楚看到的,类型声明增加了表现力,同时使代码看起来更专业。
兼容性问题
要启用类型声明模式,必须编写:
from __future__ import type_declarations
这将启用源的 Unicode 解析 [4],使 typedef 成为关键字,并强制执行所有赋值和函数调用的正确类型。
拒绝
经过仔细考虑、深刻反省、咬牙切齿和撕裂衣袍之后,决定拒绝此 PEP。
参考资料
致谢
非常感谢 Armin Ronacher、Alexander Schremmer 和 Marek Kubica,他们帮助为内置类型找到了最合适且易于记忆的声明符。
也感谢 Unicode 联盟将所有这些有用的字符包含在 Unicode 标准中。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3117.rst