Search code examples
pythonclassbuilt-in

Mimicking Python behavior for `len` and `bool`


Consider the following code:

>>> class X:
... pass
...
>>> x = X()
>>> len(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'X' has no len()
>>> bool(x)
True

But when trying to mimick this writing __len__ it does not work.

>>> class Y:
...   def __len__(self):
...     raise TypeError
...
>>> y = Y()
>>> len(y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __len__
TypeError
>>> bool(y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __len__
TypeError

Is there a way to write a __len__ function that works like it is not implemented?


Solution

  • bool() tests the truth value of an object, so you want to look at the rules for Truth Value Testing:

    By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero, when called with the object.

    You only implemented a __len__ method that's deliberately broken, raising TypeError when called. But bool() will call it when there it is an implemention and there are no other options available to determine the truth value.

    When determining the truth value, __bool__ is preferred over __len__:

    When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero.

    Demo:

    >>> class Z:
    ...     def __bool__(self):
    ...         return True
    ...     def __len__(self):
    ...         raise TypeError
    ...
    >>> len(Z())
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __len__
    TypeError
    >>> bool(Z())
    True
    

    Note that it is the len() function implementation that raises TypeError when there is no implementation for the __len__ hook. There is no exception that a __len__ implementation can raise that'll let you pretent it is not actually implemented, any exceptions it raises are propagated if it is called, because you'd normally would want to know if the implementation is broken in some way.