5

I have a set of sub-classes that should all define an attribute x that should either evaluate to True or False. In order to catch bugs when I forget to set this value in a subclass, I would like to set it in its superclass to a value for which truth evaluation results in an error. Is there any built-in value in Python with this behaviour? I sort of expected NotImplemented to have this behaviour, but it evaluates to True.

I could set it to numpy.array([0, 0]) where if x: raises ValueError, but that feels wrong. Likewise, I could define my own class where __bool__ raises an exception. But is there any built-in value appropriate for this purpose?

Other alternatives would be to set a property (abstract or not) or to not define it at all (so we get AttributeError).

gerrit
  • 24,025
  • 17
  • 97
  • 170
  • possible duplicate of [Boolean value of objects in Python](http://stackoverflow.com/questions/1087135/boolean-value-of-objects-in-python) – Cory Kramer Aug 15 '14 at 16:08
  • 1
    I'm personally a fan of the `NotImplemented` approach. It's an exception that tells you what the problem is, and you can easily add a customized message to it to clarify even farther. That said, it as always depends on your specific use case, and is also to a large degree a matter of personal preference and style. – Silas Ray Aug 15 '14 at 16:09
  • That should be `__nonzero__`, not `__bool__`, for 2.x. – jonrsharpe Aug 15 '14 at 16:10
  • @jonrsharpe `__bool__` in Python 3. – Silas Ray Aug 15 '14 at 16:11
  • @Cyber The linked post does not contain an answer to my question. I know how I can implement an object with the described behaviour; my question is if any built-in objects with this behaviour already exist. – gerrit Aug 15 '14 at 16:13
  • @SilasRay I wish `if NotImplemented:` would be an error, I think it would make a lot of sense. But it doesn`t. Creating a property that raises `NotImplementedError` would work, though. – gerrit Aug 15 '14 at 16:14
  • 3
    As you haven't actually had a clear answer to your specific question (*"Is there any builtin Python value where truth evaluation is invalid?"*): [no](https://docs.python.org/2/library/stdtypes.html#truth-value-testing). You'd have to make your own `Unbooleanable` class, which would `raise NotImplementedError` for `__nonzero__`/`__bool__`. – jonrsharpe Aug 15 '14 at 16:15
  • Another approach to keep in mind is that if this is a characteristic of the class rather than of the instance, you can raise `ValueError` or `NotImplemented` in a metaclass `__new__` if the derived type has no attribute with the required name rather than implementing it as an instance-level property. – Silas Ray Aug 15 '14 at 16:33

2 Answers2

2

I recently encountered the same question, with a slightly different use case:

I had a class with a flag attribute, the value of which is passed to __init__ by the caller. Objects of the class can be created from two different versions of data, where the older version of the data does not contain the information needed for determining whether the flag should be True of False.

Setting an otherwise bool-value to None (which is the common way of representing missing data) would not work, because None happily evaluates to False.

Like you, I didn't find a satisfactory built-in solution, so I wrote one myself.

(Written for python2.7, but easy to adjust fo python3)

class NotTrueNorFalseType(object):
    """
    A singleton class whose instance can be used in place of True or False, to represent
    a value which has no true-value nor false-value, e.g. a boolean flag the value of
    which is unknown or undefined.
    """
    def __new__(cls, *args, **kwargs):
        # singleton
        try:
            obj = cls._obj
        except AttributeError:
            obj = object.__new__(cls, *args, **kwargs)
            cls._obj = obj
        return obj

    def __nonzero__(self):
        raise TypeError('%s: Value is neither True nor False' % self)

    def __repr__(self):
        return 'NotTrueNorFalse'

NotTrueNorFalse = NotTrueNorFalseType()

The design (min-)decisions in this class were inspired by the None singleton (E.g. naming the singleton instance "Foo" and the class "FooType", returning "Foo" from __repr__, raising TypeError on invalid operations).

shx2
  • 61,779
  • 13
  • 130
  • 153
  • Why the extra complexity to make it into a singleton? Wouldn't an instance of a class which overrides `__nonzero__` have sufficed? – wim Oct 11 '16 at 17:18
  • @wim, the only reason is consistency with `True` and `False` which are singletons. I tried making it bool-like (convension-wise). – shx2 Oct 11 '16 at 18:49
  • OK, but the described use-case seemed to be avoiding the implicit "truthiness" to be inherited from object. We only want the singleton stuff if it's intended to use it for identity checks, right? (and I've never even seen anyone use that feature for bools, in fact - I'm not really sure why bools are singleton) – wim Oct 11 '16 at 19:21
1

In the future, NotImplemented will be invalid in a boolean context.

Starting with Python 3.9, using NotImplemented in a boolean context is deprecated. From the documentation:

Evaluating NotImplemented in a boolean context is deprecated. While it currently evaluates as true, it will emit a DeprecationWarning. It will raise a TypeError in a future version of Python.

As of 2020-10-06, that future version of Python has (as far as I know) not been determined, but at some point in the future (I'd expect not before Python 3.11), NotImplemented will become a builtin Python value where truth evaluation is invalid.

gerrit
  • 24,025
  • 17
  • 97
  • 170