查看: 66|回复: 3

Python Ellipsis用法:切片扩展与类型提示占位

[复制链接]
发表于 半小时前 | 显示全部楼层 |阅读模式
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。示例:
  1. class Mogic(object):
  2.     def __getitem__(self, key):
  3.         if len(key) == 3 and key[2] is Ellipsis:
  4.             d = key[1] - key[0]
  5.             r = key[0]
  6.             while True:
  7.                 yield r
  8.                 r += d
  9. ap = Mogic()  # arithmetic progression
  10. import time
  11. for i in ap[1,3,...]:
  12.     print(i)  # caution: infinity loop here
  13.     time.sleep(1)  # slow down output
复制代码
这里没有终止条件,for会一直迭代,time.sleep(1)只减慢输出,不能结束循环。排查此类代码时要检查生成器是否有break、计数上限或其他退出路径;否则就是无限循环。

【NumPy中的原地迭代】
NumPy借助Ellipsis表达“当前元素整体”,原文示例在nditer中允许读写,并用x[...] = 2*x原地修改每个元素:
  1. a = np.arange(6).reshape(2,3)
  2. a
  3. array([[0, 1, 2],
  4.        [3, 4, 5]])
  5. for x in np.nditer(a, op_flags=['readwrite']):
  6.     x[...] = 2 * x
  7. a
  8. array([[ 0,  2,  4],
  9.        [ 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]声明类型但不想初始化。示例:
  1. from typing import Callable
  2. def foo() -> Callable[..., int]:
  3.     return lambda x: 1
  4. from typing import Tuple
  5. def bar() -> Tuple[int, ...]:
  6.     return (1,2,3)
  7. def buzz() -> Tuple[int, ...]:
  8.     return (1,2,3,4)
  9. from typing import IO
  10. stream = ...  # type: IO[str]
复制代码
这些写法中,...不是运行时省略逻辑,而是类型系统或存根文件里的占位记号;具体语义由Callable、Tuple等类型构造和类型检查器解释。

【异常占位、默认参数与循环引用】
原文还列出几种场景:异常处理中不想只写pass时,可以用...作为表达式语句;函数默认参数可写成flag=...,由于Ellipsis布尔为真,if flag会进入真分支并打印Ellipsis;列表自引用时,repr会用[...]表示递归。示例:
  1. try:
  2.     1/0
  3. except ZeroDivisionError:
  4.     ...
  5. def testThreeDot(flag=...):
  6.     if flag:
  7.         print(flag)
  8.     else:
  9.         ...
  10. L = [1,2,3]
  11. L.append(L)
  12. print(L)
复制代码
最后print(L)的输出类似[1,2,3,[...]]。这些用法更多是语法占位或调试表达,生产代码中应权衡可读性,避免让同事误判为遗漏实现。

【小结】
Ellipsis是一个布尔为真的单例对象,Python 3可直接写...,Python 2只能写Ellipsis。它本身没有特殊能力,价值来自使用方在__getitem__、类型提示、stub文件或占位语句中赋予的约定。理解这些约定后,再看切片扩展、NumPy原地迭代和Type Hints占位,就能区分运行时对象与静态类型占位,避免把无限生成器、异常分支占位等写法误用到关键路径。
回复

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: Python Ellipsis用法:切片扩展与类型提示占位

这篇整理挺清楚的,尤其是把“...”拆成运行时单例和类型系统占位两条线来讲,理解起来顺很多。Mogic 那个无限生成器的提醒很实用,真要用的话最好加计数上限或 break,不然后面排查起来很容易懵。NumPy 的 x[...] 原地写回和 op_flags=['readwrite'] 也解释得到位。类型提示里 Callable[..., int]、Tuple[int, ...] 和 stub 里 stream = ... 的占位语义区分得很明白。异常分支和默认参数用 ... 确实要谨慎,可读性优先,避免同事误以为没写完。感谢分享。
回复 支持 反对

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: Python Ellipsis用法:切片扩展与类型提示占位

这篇整理得挺全,尤其是把“运行时对象”和“类型系统占位”分开讲,这点很重要。Ellipsis 本身确实没什么魔法,关键在于使用方怎么约定。 补充几个自己踩过的点:那个 ap[1,3,...] 的无限生成器,实际用的时候最好配 itertools.islice,比如取前 10 项就 islice(ap[1,3,...], 10),比在循环里写 break 更干净,也不容易忘退出条件。NumPy 里 x[...] 的写法也值得强调一下,它不是简单的 x = 2*x,而是原地写回迭代器指向的位置,所以 op_flags 必须带 readwrite,否则容易白算一场。 类型提示部分,Callable[..., int] 和 Tuple[int, ...] 里的三个点语义还不一样:前者是参数列表的占位,后者是“同类型可变长度”的标记,别混着理解。stub 文件里的 stream = ... 也要注意,它只在类型检查器眼里有意义,真跑到运行时 stream 就是 Ellipsis 本身,不能当 IO 对象用。 另外默认参数写成 flag=... 虽然能跑,但可读性确实差,团队里很容易被当成没写完。异常分支里用 ... 代替 pass 我也见过,适合临时占位,长期还是写清楚注释比较好。 整体看,这篇适合当速查,尤其适合排查“为什么这个下标返回了生成器”和“为什么类型检查过了但运行时报错”这两类问
回复 支持 反对

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: Python Ellipsis用法:切片扩展与类型提示占位

干货不少,收藏了。平时写 Python 也就用 `...` 在 `.pyi` 存根里占位,或者在 `Callable[..., int]` 里填一下参数,还真没想过它在 `__getitem__` 里能这么玩。Mogic 那个 `ap[1,3,...]` 生成等差数列的设计挺巧的,不过那个无限循环的例子确实得提醒一句,初学的朋友跑起来记得配个 `break` 或者截个长度,不然终端只能 Ctrl+C 了。另外 NumPy 里 `x[...] = 2*x` 的写法我一直在用,多维数组时比 `x[:]` 更直观地表示“整个元素”。默认参数写 `flag=...` 那个我保留意见,除非临时调试,否则真容易让队友以为漏写了实现。最后“价值来自使用方的约定”这句总结得很到位,Ellipsis 本身确实就是个特殊的单例而已。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-14 05:52 , Processed in 0.022779 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部