# Assignment to Ellipsis and NotImplemented

**URL:** <https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073>\
**Category:** Ideas\
**Tags:** documentation\
**Created:** [January 23, 2025, 2:28pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073 "2025-01-23T14:28:57Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![csucraig](https://avatars.discourse-cdn.com/v4/letter/c/ccd318/32.png) [@csucraig](https://discuss.python.org/u/csucraig)\
**Post date:** [January 23, 2025, 2:28pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/1 "2025-01-23T14:28:57Z")

</div>

Quick context, I’m a newbie here. I’m putting together a course on advanced Python (I’m a professor) and digging through various aspects of Python with an eye towards helping students make effective use of Python’s rich set of features.

I was reading about Python built-in constants (Python docs 3.13.1) and saw that two constants (Ellipsis and NotImplemented) can have their values changed at run-time. So I tried assigning to them and promptly was able to break things. For instance, taking an example that seems popular on StackOverflow for Ellipsis and breaking it:

```python
from numpy import arange
a = arange(16).reshape(2, 2, 2, 2)
print(a[Ellipsis, 0].flatten())
Ellipsis = []
print(a[Ellipsis, 0].flatten())
print(a[..., 0].flatten())

```

The first and third print give the expected result, but the second print does not…

Fiddling with NotImplemented appears to cause methods such **add** () to return actual values instead of NotImplemented, with harmful results, but I’m still trying to find a terse example.

So I’m curious, is there a good reason why we can assign new values to Ellipsis and NotImplemented rather than prohibiting it, ala True, False and None?

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [January 23, 2025, 2:57pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/2 "2025-01-23T14:57:29Z")

</div>

> [@csucraig](#):
>
> So I’m curious, is there a good reason why we can assign new values to Ellipsis and NotImplemented rather than prohibiting it, ala True, False and None?

Because they are not constants, but builtin singletons. Other things you can overwrite with harmful sideeffects:

- `int`
- `type`
- `numpy`/`np`, the symbol in the a modules globals
- `sys.modules["numpy"]`, the cached module object itself
- ` __import__ `, the entry point to the import machinery.

Python is a very dynamic language, and with very little effort you can break just about everything.

`None`, `True`, `False` are probably the three most common constants and code could become very confusing if they get overwritten, so they get a bit of special protection. But otherwise python does not prevent every stupid mistake people can make.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [January 23, 2025, 3:00pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/3 "2025-01-23T15:00:44Z")

</div>

They’re also keywords (`True`, `False`, `None`), so any bad use will be detected early during parsing, which is nice.

---

<div class="post-metadata">

**Author:** ![peterc](https://avatars.discourse-cdn.com/v4/letter/p/f9ae1b/32.png) [@peterc](https://discuss.python.org/u/peterc)\
**Post date:** [January 23, 2025, 3:22pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/4 "2025-01-23T15:22:19Z")

</div>

TBH I don’t even understand why `None` got special protection. `True` and `False` I understand as extension of the protection that is awarded to numbers. But most objects can be re-assigned, even the ones you never want to.  
It protects against the typo of `a = None = 1` instead of `a = None == b`, but it’s not like the latter is a common pattern.

Nowadays `str` and `int` need protection more than `None` does, because it’s easy to mess up

```python
x = 'a'
match x:
  case str:
    print(x)

```

and now you’ve reassigned `str` and broken all code (that follows within the same function/script) that uses `str` to convert something to a string.

---

<div class="post-metadata">

**Author:** ![Paddy3118](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/paddy3118/32/1396_2.png) [@Paddy3118](https://discuss.python.org/u/Paddy3118)\
**Post date:** [January 23, 2025, 3:33pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/5 "2025-01-23T15:33:16Z")

</div>

> [@csucraig](#):
>
> … and saw that two constants (Ellipsis and NotImplemented) can have their values changed at run-time.

It is good to tinker, but as a teacher you need to impress on students that some things should not be done, and unlike other languages, there can be less stopping you in Python than in others.  
“[We are all responsible users here](https://github.com/realpython/python-guide/issues/525)”

On a similar note, students of other languages often complain about our name-mangling for class variables. I wrote a [Rosetta Code task](https://rosettacode.org/wiki/Break_OO_privacy) on this topic which shows how class variables can be accessed for many languages.

And lastly, use of eval/exec might be an advanced topic. I wrote another [Rosetta Code task](https://rosettacode.org/wiki/Truth_table) specifically to encourage the use of eval/exec for those languages that have it. Note the Python code is much smaller than that of other languages that don’t have eval/exec.

Have fun 😁

---

<div class="post-metadata">

**Author:** ![csucraig](https://avatars.discourse-cdn.com/v4/letter/c/ccd318/32.png) [@csucraig](https://discuss.python.org/u/csucraig)\
**Post date:** [January 23, 2025, 4:09pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/6 "2025-01-23T16:09:13Z")

</div>

We seem to have slightly different views about teaching. Of course we all seek to be responsible users. At the same time (a) we all make mistakes and another post here shows a case where one can break ‘str’ – similarly someone will accidentally assign to any assignable thing and so warnings of the form “be supercareful here” is an instructor’s job (whereas if nothing broke, I could ignore the fact the constants are assignable); and (b) there are ways to avoid the perils, but you need to know the perils exist – so, in this case, using … in all cases and never using Ellipsis is safe and avoids the perils.

---

<div class="post-metadata">

**Author:** ![elis.byberi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/elis.byberi/32/35937_2.png) [@elis.byberi](https://discuss.python.org/u/elis.byberi)\
**Post date:** [January 23, 2025, 4:32pm UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/7 "2025-01-23T16:32:29Z")

</div>

> [@MegaIng](#):
>
> you can break just about everything

Or, you can override them with your own implementation.

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [January 24, 2025, 7:34am UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/8 "2025-01-24T07:34:02Z")

</div>

There are some singletons that the compiler itself uses to make control flow optimisations when compiling code (think things like optimising out “always true” and “always false” checks in conditionals). These are protected from shadowing because allowing shadowing would prohibit those optimisations (or make them potentially _very_ confusing if they were still allowed). Making the affected constants (`None`, `True`, `False`) hard keywords in the compiler was a Python 3 change: [PEP 3100 – Miscellaneous Python 3.0 Plans | peps.python.org](https://peps.python.org/pep-3100/#core-language)

Other builtins don’t need that level of protection, and being able to shadow (or even outright replace) them is sometimes useful (such as customising their behaviour for specialised environments, or intercepting calls to them for testing purposes). The fact that modifying `builtins` carelessly (such as replacing `NotImplemented` or `BaseException`) _will_ break things for an entire process, and careless shadowing can break things for a given module (hence linters often warning about it), is considered a fair trade-off for the extra flexibility that this approach offers users.

This is a general principle of the language design historically referred to as the “consenting adults” rule, but more inclusively referred to as the “responsible users” rule (since Python has _many_ non-adult users):

- “don’t do that, then” is an entirely reasonable way of handling situations where doing something unusual results in obscure exceptions
- outside security sensitive situations, we shouldn’t invest too much time in trying to anticipate the unusual things that users might attempt to do and deciding which of them we consider legitimate and which of them we think is worthy of an explicit runtime check and customised exception

`Ellipsis` specifically is a different case again, since its keyword spelling is `...` (three consecutive periods) rather than its English name. Rather than making the English name a keyword, Python 3 instead made the symbolic spelling available in all situations (it was restricted to subscripting operations in Python 2), and (as already noted above) the target of that symbolic setting _cannot_ be modified:

```python
>>> import builtins
>>> Ellipsis is ...
True
>>> builtins.Ellipsis is ...
True
>>> Ellipsis = []
>>> Ellipsis is ...
False
>>> builtins.Ellipsis is ...
True
>>> builtins.Ellipsis = Ellipsis
>>> Ellipsis is ...
False
>>> builtins.Ellipsis is ...
False

```

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [January 24, 2025, 8:04am UTC](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073/9 "2025-01-24T08:04:01Z")

</div>

`Ellipsis` cannot be made a keyword because of `ast.Ellipsis`. It is deprecated now, but there are likely uses of that name in third-party code. And there is no much benefit in this, since you always can use `...`.

`NotImplemented` is less common name, so it is safer to make it a keyword. It is also more beneficial, because it is more used in the user code, and LOAD\_CONST is faster than LOAD\_GLOBAL. The compiler can even special case `is NotImplemented` like it does for `is None`.
