Why does Python have a strange "..." object?

> This article is from the "Why Python" series, please see all articles

In the last article " Why does Python have a pass statement? ", I thought of a special way of writing, and many people will use it as a replacement for the pass statement. After the article was published, three comments did mention it.

The so-called special spelling is the following:

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

It is a half ellipsis in Chinese punctuation, which is composed of 3 dots in English. If you're seeing it for the first time, you're probably wondering: what's up with this thing? (PS: If you know it, after reading this article carefully, you may also find it strange!)

1. Know the "..." built-in constants

In fact, it is a built-in object in Python 3, with a formal name called Ellipsis, which translates to "ellipsis" in Chinese.

More precisely, it is a Built -in Constant, one of the 6 built-in constants (the others are None, False, True, NotImplemented, __debug__).

Regarding the basic nature of this object, here is a screenshot, you should be able to understand what I mean:

"..." is not mysterious, it's just a symbolic object that may be rare. Substituting it for pass is not syntactically wrong, because Python allows an object not to be referenced by assignment.

Strictly speaking, this is sidetracking and semantically untenable - putting "..." or other constants or assigned variables in an empty indented block of code, they are action-independent, It can only express "there is a useless object, don't worry about it".

Python allows these objects that are not actually used to exist, but a smart IDE should prompt (I use Pycharm), such as telling you: Statement seems to have no effect .

But the "..." constant seems to be treated specially and is not prompted by my IDE.

Many people are used to using it as a no-op like pass (an example of this usage was given in the mailing group discussion where it was first introduced). But I still tend to use pass myself, what do you think?

2. Strange Ellipsis and...

...introduced in PEP-3100 , first incorporated in Python 3.0, and Ellipsis in earlier versions.

Although it is officially said that they are two ways of writing the same object, and it is said to be a singleton (singleton), I also found a very strange phenomenon that conflicts with the description of the document:

As you can see, assigning to ... will throw an error SyntaxError: cannot assign to Ellipsis, whereas Ellipsis can be assigned, and they behave differently! After being assigned, Ellipsis's memory address and type attributes have changed, and it has become a "variable", no longer a constant.

By contrast, assigning a value to a constant such as True or None will throw an error SyntaxError: cannot assign to XXX, but assigning a value to a NotImplemented constant will not throw an error.

It is well known that in Python 2 it is also possible to assign values ​​to boolean objects (True/False), however Python 3 has made them immutable.

So there is one possible explanation: Ellipsis and NotImplemented are relics from the Python 2 era, and they can also be modified by assignment in the current version (3.8) for compatibility or just because the core developers left them out.

... was born in the era of Python 3, and may completely replace Ellipsis in the future. Currently the two coexist, and their inconsistent behavior deserves our attention. My advice: just use "..." when Ellipsis is out.

3. Why use the "..." object?

Next, let's go back to the title's question: Why does Python use the "..." object?

I'll just focus on the "..." of Python 3 here, without going back to the history and current state of Ellipsis.

I ask this question because my intention is to know: what is it useful for, and what problems does it solve? Thereby peeking into more details in the design of the Python language.

There are probably the following answers:

(1) Extended slice syntax

This description is given in the official documentation:

> Special value used mostly in conjunction with extended slicing syntax for user-defined container data types. > > This is a special value used mostly in conjunction with extended slicing syntax for custom data type containers.

The documentation does not give an example of a specific implementation, but using it in combination with the __getitem__() and slice() built-in functions, you can achieve an effect similar to [1, ..., 7] taking a slice of 7 numbers.

Since it is mainly used for data operations, it may be rarely touched by most people. I heard that Numpy uses it for some syntactic sugar usage. If you are using Numpy, can you explore what ways are there?

(2) Express "unfinished code" semantics

... can be used as placeholders, which is what I wrote in " Why does Python have a pass statement?" "The role of the pass is mentioned in. This has been partially analyzed in the previous article.

Some people think this is cute, and this idea has the support of Guido, the father of Python :

(3) Type Hint usage

Type Hint, introduced in Python 3.5, is the primary use case for "...".

It can represent parameters of variable length, such as Tuple[int, ...]represents a tuple whose elements are of type int, but the number is not limited.

It can also represent indeterminate variable types, like this example given in the documentation:

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 cannot be determined when the function is defined, the actual type of T is determined when the function is called.

In a .pyi format file, ... is everywhere. This is a stub file, which is mainly used to store type hint information of Python modules, and to check static code for type checking tools such as mypy, pytype and IDE.

(4) means infinite loop

In the end, I think there is a very ultimate reason that there is no better way than to introduce "..." to mean.

Let's look at two examples:

"..." appears in the results of both examples, what does it mean?

Containers such as lists and dictionaries store references to mutable objects if their internal elements are mutable objects. Then, when its internal elements refer to the container itself, an infinite circular reference recursively occurs.

Infinite loops cannot be represented exhaustively. Python is represented by ... , which is easier to understand. Apart from it, I am afraid there is no better choice.

Finally, let's summarize the content of this article:

  • ... is a built-in constant in Python 3, it is a singleton object, although it is another name for Ellipsis in Python 2, but its nature has parted ways with the old object
  • ... can be used as a placeholder instead of the pass statement, but as a constant object, the placeholder semantics are not strict. Many people have been accustomed to accept it, might as well use it
  • ... In many usage scenarios in Python, in addition to placeholder usage, it can also support extended slice syntax, rich Type Hint type checking, and infinite loops representing container objects
  • ... for most people, it may not be common (some people may reject it because it is a special case of symbols), but its existence can sometimes bring convenience. I hope that this article can let more people know it, then the purpose of the article will be achieved~

If you think this article is a good analysis, then you should like these articles:

1. Why does Python use indentation to divide code blocks?

2. Is Python's indentation an anti-human design?

3. Why does Python not use a semicolon as a statement terminator?

4. Why does Python not have a main function? Why do I not recommend writing the main function?

5. Why does Python recommend snake-like nomenclature?

6. Why does Python not support the i++ auto-increment syntax and the ++ operator?

7. Why does Python only need one statement "a,b=b,a" to directly exchange two variables?

8. Why does Python use the # sign as a comment?

9. Why does Python have a pass statement?

This article belongs to the "Why Python" series (produced by Python Cat), which mainly focuses on topics such as Python's syntax, design, and development. It tries to show the charming charm of Python by starting with "why" questions. All articles will be archived on Github, project address: https://github.com/chinesehuazhou/python-whydo

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324036614&siteId=291194637