本文出自“Python为什么”系列,请查看全部文章

在写上一篇《Python 为什么要有 pass 语句?》时,我想到一种特别的写法,很多人会把它当成 pass 语句的替代。在文章发布后,果然有三条留言提及了它。

所谓特别的写法就是下面这个:

# 用 ... 替代 pass
def foo():
...

它是中文标点符号中的半个省略号,也即由英文的 3 个点组成。如果你是第一次看到,很可能会觉得奇怪:这玩意是怎么回事?(PS:如果你知道它,仔细看过本文后,你同样可能会觉得奇怪!)

1、认识一下“...”内置常量

事实上,它是 Python 3 中的一个内置对象,有个正式的名字叫作——Ellipsis,翻译成中文就是“省略号”。

更准确地说,它是一个内置常量(Built-in Constant),是 6 大内置常量之一(另外几个是 None、False、True、NotImplemented、__debug__)。

关于这个对象的基础性质,下面给出了一张截图,你们应该能明白我的意思:

“...“并不神秘,它只是一个可能不多见的符号型对象而已。用它替换 pass,在语法上并不会报错,因为 Python 允许一个对象不被赋值引用。

严格来说, 这是旁门左道,在语义上站不住脚——把“...”或其它常量或已被赋值的变量放在一个空的缩进代码块中,它们是与动作无关的,只能表达出“这有个没用的对象,不用管它”。

Python 允许这些不被实际使用的对象存在,然而聪明的 IDE 应该会有所提示(我用的是 Pycharm),比如告诉你:Statement seems to have no effect

但是“...”这个常量似乎受到了特殊对待,我的 IDE 上没有作提示。

很多人已经习惯上把它当成 pass 那样的空操作来用了(在最早引入它的邮件组讨论中,就是举了这种用法的例子)。但我本人还是倾向于使用 pass,不知道你是怎么想的呢?

2、奇怪的 Ellipsis 和 ...

... 在 PEP-3100 中被引入,最早合入在 Python 3.0 版本,而 Ellipsis 则在更早的版本中就已包含。

虽然官方说它们是同一个对象的两种写法,而且说成是单例的(singleton),但我还发现一个非常奇怪的现象,与文档的描述是冲突的:

如你所见,赋值给 ... 时会报错SyntaxError: cannot assign to Ellipsis ,然而 Ellipsis 却可以被赋值,它们的行为根本就不同嘛!被赋值之后,Ellipsis 的内存地址以及类型属性都改变了,它成了一个“变量”,不再是常量。

作为对比,给 True 或 None 之类的常量赋值时,会报错SyntaxError: cannot assign to XXX,但是给 NotImplemented 常量赋值时不会报错。

众所周知,在 Python 2 中也可以给布尔对象(True/False)赋值,然而 Python 3 已经把它们改造成不可修改的。

所以有一种可能的解释:Ellipsis 和 NotImplemented 是 Python 2 时代的遗留产物,为了兼容性或者只是因为核心开发者遗漏了,所以它们在当前版本(3.8)中还可以被赋值修改。

... 出生在 Python 3 的时代,或许在将来会完全取代 Ellipsis。目前两者共存,它们不一致的行为值得我们注意。我的建议:只使用"..."吧,就当 Ellipsis 已经被淘汰了。

3、为什么要使用“...”对象?

接下来,让我们回到标题的问题:Python 为什么要使用“...”对象?

这里就只聚焦于 Python 3 的“...”了,不去追溯 Ellipsis 的历史和现状。

之所以会问这个问题,我的意图是想知道:它有什么用处,能够解决什么问题?从而窥探到 Python 语言设计中的更多细节。

大概有如下的几种答案:

(1)扩展切片语法

官方文档中给出了这样的说明:

Special value used mostly in conjunction with extended slicing syntax for user-defined container data types.

这是个特殊的值,通常跟扩展的切片语法相结合,用在自定义的数据类型容器上。

文档中没有给出具体实现的例子,但用它结合__getitem__() 和 slice() 内置函数,可以实现类似于 [1, ..., 7] 取出 7 个数字的切片片段的效果。

由于它主要用在数据操作上,可能大部分人很少接触。听说 Numpy 把它用在了一些语法糖用法上,如果你在用 Numpy 的话,可以探索一下都有哪些玩法?

(2)表达“未完成的代码”语义

... 可以被用作占位符,也就是我在《Python 为什么要有 pass 语句?》中提到 pass 的作用。前文中对此已有部分分析。

有人觉得这样很 cute,这种想法获得了 Python 之父 Guido 的支持

(3)Type Hint 用法

Python 3.5 引入的 Type Hint 是“...”的主要使用场合。

它可以表示不定长的参数,比如Tuple[int, ...] 表示一个元组,其元素是 int 类型,但数量不限。

它还可以表示不确定的变量类型,比如文档中给出的这个例子:

from typing import TypeVar, Generic

T = TypeVar('T')

def fun_1(x: T) -> T: ...  # T here
def fun_2(x: T) -> T: ... # and here could be different fun_1(1) # This is OK, T is inferred to be int
fun_2('a') # This is also OK, now T is str

T 在函数定义时无法确定,当函数被调用时,T 的实际类型才被确定。

在 .pyi 格式的文件中,... 随处可见。这是一种存根文件(stub file),主要用于存放 Python 模块的类型提示信息,给 mypy、pytype 之类的类型检查工具 以及 IDE 来作静态代码检查。

(4)表示无限循环

最后,我认为有一个非常终极的原因,除了引入“...”来表示,没有更好的方法。

先看看两个例子:

两个例子的结果中都出现了“...”,它表示的是什么东西呢?

对于列表和字典这样的容器,如果其内部元素是可变对象的话,则存储的是对可变对象的引用。那么,当其内部元素又引用容器自身时,就会递归地出现无限循环引用。

无限循环是无法穷尽地表示出来的,Python 中用 ... 来表示,比较形象易懂,除了它,恐怕没有更好的选择。

最后,我们来总结一下本文的内容:

  • ... 是 Python 3 中的一个内置常量,它是一个单例对象,虽然是 Python 2 中就有的 Ellipsis 的别称,但它的性质已经跟旧对象分道扬镳
  • ... 可以替代 pass 语句作为占位符使用,但是它作为一个常量对象,在占位符语义上并不严谨。很多人已经在习惯上接受它了,不妨一用
  • ... 在 Python 中不少的使用场景,除了占位符用法,还可以支持扩展切片语法、丰富 Type Hint 类型检查,以及表示容器对象的无限循环
  • ... 对大多数人来说,可能并不多见(有人还可能因为它是一种符号特例而排斥它),但它的存在,有些时候能够带来便利。希望本文能让更多人认识它,那么文章的目的也就达成了~

如果你觉得本文分析得不错,那你应该会喜欢这些文章:

1、Python为什么使用缩进来划分代码块?

2、Python 的缩进是不是反人类的设计?

3、Python 为什么不用分号作语句终止符?

4、Python 为什么没有 main 函数?为什么我不推荐写 main 函数?

5、Python 为什么推荐蛇形命名法?

6、Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?

7、Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?

8、Python 为什么用 # 号作注释符?

9、Python 为什么要有 pass 语句?

本文属于“Python为什么”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。所有文章将会归档在 Github 上,项目地址:https://github.com/chinesehuazhou/python-whydo

最新文章

  1. Android调用默认浏览器打开指定Url
  2. Android模拟器Genymotion如何访问本地服务器?
  3. NET代码重构
  4. Mac安装win7
  5. Linux异步IO【转】
  6. 不让input表单的输入框保存用户输入的历史记录
  7. 使用Java创建RESTful Web Service(转)
  8. JAVA 操作 DBF 文件数据库
  9. Macbook怎么强制关闭后台程序?Macbook强制关闭后台程序的方法
  10. LAMP 版本查看
  11. TensorFlow从入门到理解
  12. Java11实战:模块化的 Netty RPC 服务项目
  13. leecode第五十四题(螺旋矩阵)
  14. 转:openwrt 框架分析
  15. PHP 下载中文乱码解决
  16. js 滑动门的实现
  17. Windows环境下Qwt安装和使用
  18. 【Java】得到当前系统时间,精确到毫秒
  19. XSS练习小游戏和答案参考
  20. Windows下部署ElasticSearch5.0以下版本

热门文章

  1. 洛谷 P1640 SCOI2010 连续攻击游戏 并查集
  2. Django setting设置 常用设置
  3. 08 Flask源码剖析之flask拓展点
  4. 数据可视化实例(十四):面积图 (matplotlib,pandas)
  5. 小书MybatisPlus第4篇-表格分页与下拉分页查询
  6. nginx一个端口配置多个不同服务映射
  7. Java开发中的eclispe常用快捷键&全部快捷键
  8. ASP.NET Core静态文件处理源码探究
  9. T133309 57级返校测试重测-T2-选票统计
  10. msf stagers开发不完全指北(四): msf 中使用域前置技术隐藏流量