Python 3里的三个点...并不是普通文本省略号,而是内置的Ellipsis单例对象。交互式解释器中输入...会得到Ellipsis,type(...)为<class 'ellipsis'>,bool(...)为True,多次id(...)结果相同。Python 2没有...语法,只能直接写Ellipsis取得该对象。原文指出它自身没有特殊方法或属性,官方文档称其通常用于扩展切片功能,但描述较模糊。
【切片调用与__getitem__】
lst[1:3]这类切片最终会把slice(1,3,None)传给__getitem__(self,key);字典取值a[k]也使用__getitem__。因此库或自定义类可以在__getitem__中识别Ellipsis,扩展下标协议。原文的Mogic类用ap[1,3,...]触发等差数列生成器:key收到元组(1,3,Ellipsis),当len(key)==3且key[2] is Ellipsis时,计算d=key[1]-key[0]、r=key[0],然后不断yield r并r+=d。示例:- class Mogic(object):
- def __getitem__(self, key):
- if len(key) == 3 and key[2] is Ellipsis:
- d = key[1] - key[0]
- r = key[0]
- while True:
- yield r
- r += d
- ap = Mogic() # arithmetic progression
- import time
- for i in ap[1,3,...]:
- print(i) # caution: infinity loop here
- time.sleep(1) # slow down output
复制代码 这里没有终止条件,for会一直迭代,time.sleep(1)只减慢输出,不能结束循环。排查此类代码时要检查生成器是否有break、计数上限或其他退出路径;否则就是无限循环。
【NumPy中的原地迭代】
NumPy借助Ellipsis表达“当前元素整体”,原文示例在nditer中允许读写,并用x[...] = 2*x原地修改每个元素:- a = np.arange(6).reshape(2,3)
- a
- array([[0, 1, 2],
- [3, 4, 5]])
- for x in np.nditer(a, op_flags=['readwrite']):
- x[...] = 2 * x
- a
- array([[ 0, 2, 4],
- [ 6, 8, 10]])
复制代码 op_flags=['readwrite']的含义是迭代器允许写回;x[...]把计算结果赋给x对应位置,最终a的元素翻倍。原文说明NumPy对Ellipsis还有更多用法,实现机制同样是在__getitem__中做判断。
【Type Hints中的占位】
PEP 484在Python 3.5加入Type Hints。原文给出Ellipsis在类型提示中的几种用法:Callable[..., int]在不确定参数签名时占位;Tuple[int, ...]表示同类型不定长元组;.pyi stub文件中用stream = ... # type: IO[str]声明类型但不想初始化。示例:- from typing import Callable
- def foo() -> Callable[..., int]:
- return lambda x: 1
- from typing import Tuple
- def bar() -> Tuple[int, ...]:
- return (1,2,3)
- def buzz() -> Tuple[int, ...]:
- return (1,2,3,4)
- from typing import IO
- stream = ... # type: IO[str]
复制代码 这些写法中,...不是运行时省略逻辑,而是类型系统或存根文件里的占位记号;具体语义由Callable、Tuple等类型构造和类型检查器解释。
【异常占位、默认参数与循环引用】
原文还列出几种场景:异常处理中不想只写pass时,可以用...作为表达式语句;函数默认参数可写成flag=...,由于Ellipsis布尔为真,if flag会进入真分支并打印Ellipsis;列表自引用时,repr会用[...]表示递归。示例:- try:
- 1/0
- except ZeroDivisionError:
- ...
- def testThreeDot(flag=...):
- if flag:
- print(flag)
- else:
- ...
- L = [1,2,3]
- L.append(L)
- print(L)
复制代码 最后print(L)的输出类似[1,2,3,[...]]。这些用法更多是语法占位或调试表达,生产代码中应权衡可读性,避免让同事误判为遗漏实现。
【小结】
Ellipsis是一个布尔为真的单例对象,Python 3可直接写...,Python 2只能写Ellipsis。它本身没有特殊能力,价值来自使用方在__getitem__、类型提示、stub文件或占位语句中赋予的约定。理解这些约定后,再看切片扩展、NumPy原地迭代和Type Hints占位,就能区分运行时对象与静态类型占位,避免把无限生成器、异常分支占位等写法误用到关键路径。 |