Peculiar Python 1

12 Jul 2024

Disclaimer: The title sounds clickbait-y but I used it because I like alliterations. The behaviour of code described here is not peculiar, it is exactly as expected from the specifications. However, it is not something that we come across day to day.

Not very long ago, I got a code snippet from Mustaque, a brilliant friend and a colleague of mine, and a question whether I can explain why the code is behaving in the way it is. (I still suspect he knew the answer and was just messing with me). This post is my answer to the question.

Here is the above-mentioned snippet:

~ % python3
>>> NotImplemented and True
<stdin>:1: DeprecationWarning: NotImplemented should not be used in a boolean context
True
>>> NotImplemented = True
False
>>> ... and True
True
>>> ... and False
False
>>> True and ...
Ellipsis
>>> False and ...
False

Breaking down what we see:

  • There were two not-so-normal constants here: NotImplemented and ... (Ellipsis)
  • We are doing boolean operations on them, and we see that they are truthy i.e., they evaluate to true in a boolean expression.
  • The return value differs based on the order of the operators in the boolean operations.

What is NotImplemented?

NotImplemented is a built-in constant i.e., a special value in Python. We use it when we are defining the binary special methods of a class (e.g.__eq__(), __lt__(), __add__(), __rsub__(), __imul__(), __iand__(), etc.) to let the interpreter know that the operation is not implemented with the other type.

Let me clarify what I’m talking about with an example:

class Coords:
    def __init__(self, x:int, y:int) -> None:
        self.x = x
        self.y = y
    
    def __lt__(self, other):
        if not isinstance(other, Coords):
            return NotImplemented
        return self.x < other.x and self.y < other.y
        

a = Coords(1,2)
b = Coords(3,4)

print(a<b)
print(b<a)
print(a<2)

Output of the above snippet:

True
False
Traceback (most recent call last):
  File "~/Code-Shown-In-Blog/Peculiar Python/1.py", line 17, in <module>
    print(a<2)
          ^^^
TypeError: '<' not supported between instances of 'Coords' and 'int'

As you can see, I use NotImplemented to tell the interpreter that I have not implemented comparison between Coords and any other type.

What is …?

, which evaluates to Ellipsis is another built-in constant in Python. It has no single expected behaviour, but is used in multiple contexts:

  • As a replacement for pass when defining yet to be implemented functions.
def func():
 ...
  • Some uses in typing which I have not personally used/explored.
  • Some third party libraries use it to denote some custom behaviour. Ex: Pydantic uses it to denote a field which is required but can be set to None

Why are NotImplemented and Ellipsis Truthy?

They are truthy because they are not falsy. Now that you are slightly annoyed, I’ll explain it better.

In Python, any object can be tested for truth value, and by default it considers all objects to be true unless its class defines a __bool()__ which returns False or __len()__ which returns 0. Below is the list of built-in constants which are considered falsy.

  • constants defined to be false: None and False
  • zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • empty sequences and collections: '', (), [], {}, set(), range(0)

Since, NotImplemented and Ellipsis do not meet the criteria for falsiness, they are truthy. (NotImplemented will soon become falsy, linking the discussion and PR detailing why)

Why does the Order of Operators decide the Return Value in Boolean Operations?

You’ll hate me for this but it because that’s how it is supposed to be. As defined in the Python docs, the boolean operators work the following way:

Operation Result
x or y if x is true, then x, else y
x and y if x is false, then x, else y
not x if x is false, then True, else False

So, the return value is based on the above logic.

I have explained the code snippet to the best of sleep-deprived abilities, please feel free to talk to me if you have feedback or comments. As the dolphins say, So long, and thanks for all the fish.

Tags