While idly surfing the namespace I noticed an odd looking object called Ellipsis
, it does not seem to be or do anything special, but it's a globally available builtin.
After a search I found that it is used in some obscure variant of the slicing syntax by Numpy and Scipy... but almost nothing else.
Was this object added to the language specifically to support Numpy + Scipy? Does Ellipsis have any generic meaning or use at all?
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
x=[];x.append(x);print(x)
, to see how it handled stringifying cyclical objects. It returned [[...]]
. I thought "I wonder what happens if I type in [[...]]
? My guess was it would throw a syntax error. Instead, it returned [[Ellipsis]]
. Python is so weird. The Google search that ensued brought me to this page.
...
in a recursive repr is just a placeholder and has no relation to Ellipsis
This came up in another question recently. I'll elaborate on my answer from there:
Ellipsis is an object that can appear in slice notation. For example:
myList[1:2, ..., 0]
Its interpretation is purely up to whatever implements the __getitem__
function and sees Ellipsis
objects there, but its main (and intended) use is in the numpy third-party library, which adds a multidimensional array type. Since there are more than one dimensions, slicing becomes more complex than just a start and stop index; it is useful to be able to slice in multiple dimensions as well. E.g., given a 4 × 4 array, the top left area would be defined by the slice [: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]])
Extending this further, Ellipsis is used here to indicate a placeholder for the rest of the array dimensions not specified. Think of it as indicating the full slice [:]
for all the dimensions in the gap it is placed, so for a 3d array, a[..., 0]
is the same as a[:, :, 0]
and for 4d a[:, :, :, 0]
, similarly, a[0, ..., 0]
is a[0, :, :, 0]
(with however many colons in the middle make up the full number of dimensions in the array).
Interestingly, in python3, the Ellipsis literal (...
) is usable outside the slice syntax, so you can actually write:
>>> ...
Ellipsis
EDIT: Ellipsis is also used in the standard library typing
module: e.g. Callable[..., int]
to indicate a callable that returns an int
without specifying the signature, or tuple[str, ...]
to indicate a variable-length homogeneous tuple of strings.
In Python 3, you can¹ use the Ellipsis literal ...
as a “nop” placeholder for code that hasn't been written yet:
def will_do_something():
...
This is not magic; any expression can be used instead of ...
, e.g.:
def will_do_something():
1
(Can't use the word “sanctioned”, but I can say that this use was not outrightly rejected by Guido.)
¹ 'can' not in {'must', 'should'}
...
used where people want to indicate something they intend to fill in later (a 'todo' empty block) and pass
to mean an block intended to have no code.
NotImplemented
literal, which is useful when you want your incomplete function to return something meaningful (instead of None
as in your example). (Another usecase: Implementing arithmetic operations)
NotImplemented = 'something_else'
is valid python, but ... = 'something_else'
is a syntax error.
NotImplemented
is not intended to be an alternative to None
. Its usage is rather narrow. See documentation here
As of Python 3.5 and PEP484, the literal ellipsis is used to denote certain types to a static type checker when using the typing module.
Example 1:
Arbitrary-length homogeneous tuples can be expressed using one type and ellipsis, for example Tuple[int, ...]
Example 2:
It is possible to declare the return type of a callable without specifying the call signature by substituting a literal ellipsis (three dots) for the list of arguments:
def partial(func: Callable[..., str], *args) -> Callable[..., str]:
# Body
Summing up what others have said, as of Python 3, Ellipsis is essentially another singleton constant similar to None
, but without a particular intended use. Existing uses include:
In slice syntax to represent the full slice in remaining dimensions
In type hinting to indicate only part of a type(Callable[..., int] or Tuple[str, ...])
In type stub files to indicate there is a default value without specifying it
Possible uses could include:
As a default value for places where None is a valid option
As the content for a function you haven't implemented yet
...
as a default value. None
at least conveys the semantic meaning of "there was no value passed"; ...
doesn't. Alternate sentinels are typically purpose-made instances of object
or a custom class, meant to be tested against with is
. See, for example, the dataclasses
module, which defines several custom sentinels in this way.
Ellipsis
is not the same as None
, as you know.
...
as the default value for parameters (to indicate that nothing was really passed to that parameter) in functions that also deal with None
values. def function(param1 = 5, param2 = ...): if not param2 is Ellipsis: #do something else: #do something else
- This function may have something to do with None
values being passed in, for instance. Hence we are using ...
or Ellipsis
here as a default, and it is justified since that object doesn't have an actual use (not for me, atleast)
You can also use the Ellipsis when specifying expected doctest output:
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
...
are syntactic, and don't use
the actual Ellipsis object. Isn't it really nothing more than a handy name for an adaptable concept?
...
.
From the Python documentation:
This object is commonly used by slicing (see Slicings). It supports no special operations. There is exactly one ellipsis object, named Ellipsis (a built-in name). type(Ellipsis)() produces the Ellipsis singleton. It is written as Ellipsis or ....
__getitem__
minimal ...
example in a custom class
When the magic syntax ...
gets passed to []
in a custom class, __getitem__()
receives a Ellipsis
class object.
The class can then do whatever it wants with this Singleton object.
Example:
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)
# 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] == (1, slice(2,3,4), Ellipsis, 6)
The Python built-in list
class chooses to give it the semantic of a range, and any sane usage of it should too of course.
Personally, I'd just stay away from it in my APIs, and create a separate, more explicit method instead.
Tested in Python 3.5.2 and 2.7.12.
For anyone coming to this answer from working in a codebase with heavy Pydantic use: this is also how Pydantic indicates a field that is required but can be set to None
, which they refer to as "required optional fields". This is why they end up used in FastAPI, too.
You can use Ellipsis yourself, in custom slicing situations like numpy has done, but it has no usage in any builtin class.
I don't know if it was added specifically for use in numpy, but I certainly haven't seen it used elsewhere.
See also: How do you use the ellipsis slicing syntax in Python?
As mentioned by @noɥʇʎԀʎzɐɹƆ and @phoenix - You can indeed use it in stub files. e.g.
class Foo:
bar: Any = ...
def __init__(self, name: str=...) -> None: ...
More information and examples of how to use this ellipsis can be discovered here https://www.python.org/dev/peps/pep-0484/#stub-files
None
.
module.pyi
exists for static type checking, it is not executable code. It is used to add type information to modules which do not have it. In this usage def foo(bar:str=...)->str
it indicates there is a default value (i.e. the argument is optional) without indicating what the default is.
bar: Any = ...
and -> None: ...
in the code above? Oh, I think the ...
after -> None:
just indicates the body of the method maybe?
bar: Any = ...
means Foo
has a member called bar
of type Any
, with an unspecified default value. pyi files conventionally do this.
Its intended use shouldn't be only for these 3rd party modules. It isn't mentioned properly in the Python documentation (or maybe I just couldn't find that) but the ellipsis ...
is actually used in CPython in at least one place.
It is used for representing infinite data structures in Python. I came upon this notation while playing around with lists.
See this question for more info.
ellipsis
built-in type and the Ellipsis
object. Representing infinite data structures with ellipses is purely for display, having nothing to do with ellipsis
type or Ellipsis
object.
__repr__
strings aim to be valid Python expressions - if it wasn't for ellipsis existing in the language as it does, the representation wouldn't be a valid expression.
eval(repr(a))
aim to be equal to a
. Unfortunately it's false from time to time in practice, even for built-in types. Try this out: a=[]; a.append(a); eval(repr(a))
. repr(a)
is [[...]]
, invalid expression in Python 2. (In Python 3 it's valid, but the eval result is something different, still contrary to the original intention.)
This is equivalent.
l=[..., 1,2,3]
l=[Ellipsis, 1,2,3]
...
is a constant defined inside built-in constants
.
Ellipsis The same as the ellipsis literal “...”. Special value used mostly in conjunction with extended slicing syntax for user-defined container data types.
In typer ...
is used to create required parameters: The Argument
class expects a default value, and if you pass the ...
it will complain if the user does not pass the particular argument.
You could use None
for the same if Ellipsis
was not there, but this would remove the opportunity to express that None
is the default value, in case that made any sense in your program.
FastAPI makes use of the Ellipsis for creating required Parameters. https://fastapi.tiangolo.com/tutorial/query-params-str-validations/
Success story sharing
typing
module: e.g.Callable[..., int]
to indicate a callable that returns anint
without specifying the signature, orTuple[str, ...]
to indicate a variable-length homogeneous tuple of strings.a = [1, 2]; a[0] = a; print(a)
gives[[...], 2]
. Is this the same thing or a different use?