省略号对象是做什么用的?

850
当我闲逛命名空间时,我注意到一个叫做“Ellipsis”的奇怪对象,它似乎没有任何特殊的作用或功能,但是它是一个全局可用的内置对象。
经过搜索,我发现它在Numpy和Scipy的某些晦涩的变体切片语法中被使用...... 但几乎没有其他用途。
这个对象是被添加到语言中以支持Numpy + Scipy吗?Ellipsis是否有任何通用的含义或用途?
D:\workspace\numpy>python
Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> Ellipsis
Ellipsis

3
请参阅 https://dev59.com/2XRB5IYBdhLWcg3wAjJH 的答案。 - Patrick McElhaney
49
我这样发现的:我输入了“x=[];x.append(x);print(x)”来查看它如何处理循环对象的字符串化。 它返回了“[[...]]”。 我想:“如果我输入[[...]]会发生什么?” 我猜测会抛出语法错误。但是,它返回了“[[Ellipsis]]”。Python真是太奇怪了。随后进行的谷歌搜索将我带到了这个页面。 - Cyoce
53
请注意,递归 repr 中的 ... 只是一个占位符,并且与 Ellipsis 没有任何关系。 - Eevee
40
顺便提一下,在导入中使用三个点(...)表示“从上两级包中导入”。 - Mad Physicist
2
@croq https://dev59.com/PlwY5IYBdhLWcg3wpJA-。https://dev59.com/6HNA5IYBdhLWcg3wNa2T。这两个链接应该能够解释一切,答案中还包含了文档和PEP的适当链接。 - Mad Physicist
显示剩余5条评论
14个回答

838

这个问题最近在另一个问题中出现。我将从那里详细解释我的答案:

Ellipsis是可以在切片符号中出现的对象。例如:

myList[1:2, ..., 0]

它的解释完全取决于实现__getitem__函数并在那里看到Ellipsis对象的内容,但其主要(也是预期的)用途是在第三方库numpy中,该库添加了多维数组类型。由于存在多个维度,因此对于切片的操作比仅使用起始和停止索引更为复杂;能够在多个维度上进行切片非常有用。例如,给定一个4×4的数组,左上角区域将由切片[:2, :2]定义:

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12],
       [13, 14, 15, 16]])

>>> a[:2, :2]  # top left
array([[1, 2],
       [5, 6]])

进一步扩展,省略号 (Ellipsis) 在这里用于表示未指定的数组维度的占位符。它表示在其所放置的间隙中对所有维度使用完整切片 [:] ,因此对于一个三维数组,a[..., 0]a[:, :, 0] 相同,对于四维数组,则为 a[:, :, :, 0] ,类似地,a[0, ..., 0] 等同于 a[0, :, :, 0] (其中每个冒号代表数组中剩余的维数)。

有趣的是,在Python3中,省略号字面量 (...) 可以在切片语法之外使用,因此您实际上可以编写:

>>> ...
Ellipsis

编辑:省略号也被用于标准库中的typing模块:例如,Callable[..., int]表示返回int的可调用对象,但没有指定签名;或者tuple [str,...]表示一个长度可变的由字符串组成的同构元组。


43
在存根文件中,PEP 484类型提示也会使用它。 - noɥʇʎԀʎzɐɹƆ
27
了解,FastAPI框架(适用于Python 3.6+)现在也使用它。https://fastapi.tiangolo.com/tutorial/query-params-str-validations/ - Andrew Allaire
5
@ArtOfWarfare,你说得完全正确。这是来自一个口头上说出“省略号”的人的认可,而不是在句子之间停顿。 - PrimeRibeyeDeal
4
我找到了这个。当你在一个列表中进行自我引用(循环引用)时,似乎会出现这种情况:a = [1, 2]; a[0] = a; print(a)会输出[[...], 2]。这是相同的情况还是另一种用法? - Bill
3
这是一个“随便什么”。你知道标志有整数默认值并且有一个主体,但由于它是操作系统特定的,所以你不知道它是什么。当然,socket 实际上是用 C 实现的,你不想添加类型信息,因为这样做太过繁琐了。 - Matthias Urlichs
显示剩余3条评论

375
在Python 3中,您可以使用省略号文字"..."作为“nop”占位符,用于尚未编写的代码。¹
def will_do_something():
    ...

这并不是魔法,任何表达式都可以替代...,例如:

def will_do_something():
    1

我不能使用"受到制裁"这个词,但是我可以说Guido并没有直接拒绝这种用法。

¹ 'can' not in {'must', 'should'}


274
在半正式的场合中,我经常看到人们在想要表示稍后填充内容的空块(即待完成的空块)时使用“...”,而使用“pass”表示一个不打算编写代码的空块。 - Gareth Latty
42
Python还有一个NotImplemented字面常量,当你想让未完成的函数返回有意义的内容时(而不是像在你的示例中返回None),它非常有用。(另一个用例:实现算术运算 - zvyn
27
那不是文字意义上的,只是一个名称。例如 NotImplemented = 'something_else' 是有效的 Python 代码,但 ... = 'something_else' 则是语法错误。 - wim
16
"NotImplemented"并不是作为"None"的替代品而设计的,它的使用范围相当狭窄。请参考这里的文档说明。 - hans
8
在Python 3中,“raise NotImplementedError”仍然存在。 - tzot
显示剩余9条评论

132

从Python 3.5和PEP484开始,当使用typing模块时,字面省略号用于在静态类型检查器中表示某些类型。

示例1:

可以使用一个类型和省略号来表示任意长度的同类元组,例如Tuple[int, ...]

示例2:

可以通过将三个点的字面值替换为参数列表来声明可调用对象的返回类型,而不指定调用签名:

def partial(func: Callable[..., str], *args) -> Callable[..., str]:
    # Body

例子1. tuple[int]tuple 有什么区别? - Hello Human
tuple[int] 表示一个只包含一个整数的元组(例如:(1,))。tuple[int, ...] 表示一个包含零个或多个整数的元组,比如:(1, 2, 3) - phoenix
啊,好的,元组就像是一种“特殊情况”似乎:https://docs.python.org/3/library/typing.html#annotating-tuples - Hello Human

72

总结他人的说法,从Python 3开始,Ellipsis本质上是另一个类似于None的单例常量,但没有特定的使用目的。现有的用途包括:

  • 在切片语法中表示剩余维度中的完整切片
  • 在类型提示中指示类型的一部分(Callable[..., int]Tuple[str, ...]
  • 在类型存根文件中表示存在默认值,但未指定该默认值

可能的用途包括:

  • 作为None的有效选项的默认值
  • 作为你尚未实现的函数的内容

作为 None 是有效选项的地方的默认值。 - Ben
我不会将 ... 作为默认值。None 至少传达了“没有传递值”的语义含义;而 ... 则没有。备用标记通常是 object 的特定实例或自定义类,旨在使用 is 进行测试。例如,请参见 dataclasses 模块,该模块以此方式定义了几个自定义标记。 - chepner
“作为None是有效选项的默认值” -> 您是在谈论类型提示或存根文件吗?您能否详细说明一下?在正常代码中,“省略号”不同于“None”,您知道的。 - starriet
1
我认为他们的意思是在处理None值的函数中,将...用作参数的默认值(表示未真正传递该参数)。 def function(param1 = 5, param2 = ...): if not param2 is Ellipsis: #do something else: #do something else
  • 这个函数可能与传递None值有关。因此,在这里使用...Ellipsis作为默认值是合理的,因为该对象没有实际用途(至少对我来说是这样)。
- a_n
我写了一个ORM实用程序。ORM.update(db, obj)接收一个对象obj以传递要更新的字段和值。如果其中一个字段obj.x = None,则表示SET x = null,如果obj.x = ...,则表示x不是要更新的字段。 - John Lin

49

当您指定期望的doctest输出时,也可以使用省略号:

class MyClass(object):
    """Example of a doctest Ellipsis

    >>> thing = MyClass()
    >>> # Match <class '__main__.MyClass'> and <class '%(module).MyClass'>
    >>> type(thing)           # doctest:+ELLIPSIS
    <class '....MyClass'>
    """
    pass

16
这是否涉及到省略号对象?还是仅属于doctest解析器/匹配器的一个特性? - akaihola
3
@akaihola,我认为它确实如doctest.ELLIPSIS所描述的那样。我预计大多数使用...的情况都是语法上的,并且不会use实际的省略号对象。它难道不仅仅是一个适应性概念的方便名称吗? - nealmcb
这只是指文本中的省略号,而不是 Python 字面值 ... - chepner

39
Python文档中:

这个对象通常用于切片(参见切片)。它不支持任何特殊操作。恰好有一个省略对象,名为Ellipsis(内置名称)。type(Ellipsis)()会生成省略单例。

它的写法可以是Ellipsis或者...


30

对于在使用大量Pydantic的代码库中查看此答案的人:这也是Pydantic指示一个字段是必需的但可以设置为None的方式,他们称之为“必须可选字段”。 这也是它们在FastAPI中的使用原因。


3
是的,这是我自己代码中需要的一个应用案例;下次需要时会使用它。 - Cornel Masson

29

__getitem__ 在自定义类中的最小示例

当使用魔法语法...在一个自定义类中传递给[]时,__getitem__()将接收到一个Ellipsis类对象。

然后该类可以对此单例对象执行任何操作。

示例:

class C(object):
    def __getitem__(self, k):
        return k

# Single argument is passed directly.
assert C()[0] == 0

# Multiple indices generate a tuple.
assert C()[0, 1] == (0, 1)

# Slice notation generates a slice object.
assert C()[1:2:3] == slice(1, 2, 3)

# Empty slice entries become None.
assert C()[:2:] == slice(None, 2, None)

# Ellipsis notation generates the Ellipsis class object.
# Ellipsis is a singleton, so we can compare with `is`.
assert C()[...] is Ellipsis

# Everything mixed up.
assert C()[1, 2:3:4, ..., 6, :7:, ..., 8] == \
       (1, slice(2,3,4), Ellipsis, 6, slice(None,7,None), Ellipsis, 8)

Python内置的list类选择给它一个范围的语义,当然,任何明智的使用方式也应该这样做。

个人而言,在我的API中我会避开它,而代之以创建一个更具体的方法。

在Python 3.5.2和2.7.12中进行了测试。


2
使用[-O]参数运行此代码,您的代码将始终运行;D - moshevi
@moshevi 我已经使用 sudo cp /usr/bin/true /usr/bin/python3 好几年了,我的老板们还没有注意到。 - Ciro Santilli OurBigBook.com

14

正如 @noɥʇʎԀʎzɐɹƆ 和 @phoenix 所提到的 - 您确实可以在存根文件中使用它。例如:

class Foo:
    bar: Any = ...
    def __init__(self, name: str=...) -> None: ...

更多关于如何使用省略号的信息和示例可以在这里找到 https://www.python.org/dev/peps/pep-0484/#stub-files


2
好的,那么当我实例化该类并且省略号代码运行时会发生什么? - Robert
1
@Robert 什么都没有发生。把它看作是“None”。 - 157 239n
1
@Robert,存根文件(例如module.pyi)用于静态类型检查,它不是可执行代码。它用于向没有类型信息的模块添加类型信息。在这种用法中,def foo(bar:str=...)->str表示有一个默认值(即该参数是可选的),但不指定默认值是什么。 - Ben
@Ben 简单明了的解释 :) 顺便问一下,代码中的 bar: Any = ...-> None: ... 是什么意思?哦,我想 -> None: 后面的 ... 只是表示方法体吧? - starriet
1
@starriet bar: Any = ... 表示 Foo 有一个名为 bar 的成员,类型为 Any,默认值未指定。pyi 文件通常会这样做。 - Ben
显示剩余2条评论

14

你可以在自定义切片情况下像numpy一样使用省略号,但它在任何内置类中都没有用途。

我不知道它是否是专门为numpy添加的,但我肯定没有在其他地方看到过它被使用。

另见:如何在Python中使用省略号切片语法?


现在它也被PyTorch和其他库所使用。 - Luca Di Liello

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接