Booleans have two possible values. Are there types that have three possible values? [duplicate] Booleans have two possible values. Are there types that have three possible values? [duplicate] python python

Booleans have two possible values. Are there types that have three possible values? [duplicate]


In Python I'd do that with a wrapper object that holds one of those three values; I'd use True, False, and None. Since the implicit truthiness value of a Boolean-like object with three possible values is problematic, we'll solve that by disallowing that entirely (raising an exception in __nonzero__(), or in Python 3, __bool__()), thus requiring that comparisons always be done explicitly, using in, ==, or !=. We'll implement equality as identity so that only the specific singleton values True, False, and None are matched.

class Tristate(object):    def __init__(self, value=None):       if any(value is v for v in (True, False, None)):          self.value = value       else:           raise ValueError("Tristate value must be True, False, or None")    def __eq__(self, other):       return (self.value is other.value if isinstance(other, Tristate)               else self.value is other)    def __ne__(self, other):       return not self == other    def __nonzero__(self):   # Python 3: __bool__()       raise TypeError("Tristate object may not be used as a Boolean")    def __str__(self):        return str(self.value)    def __repr__(self):        return "Tristate(%s)" % self.value

Usage:

t = Tristate(True)t == True           # Truet != False          # Truet in (True, False)  # Truebool(t)             # Exception!if t: print "woo"   # Exception!

When using Tristate objects, you must explicitly specify which values to match, i.e. foo == True or bar != None. You can also do foo in (False, None) to match multiple values (though of course in two values is the opposite of != with a single value). If there are other logic operations you wish to be able to perform with these objects, you could implement these as methods, or possibly by overriding certain operators (sadly, however, logical not, and, and or are not overrideable, though there's a proposal to add that).

Also note that you can't override id() in Python, so e.g. Tristate(None) is None is False; the two objects are in fact different. Since good Python style is to use is when comparing against singletons, this is unfortunate, but unavoidable.

Edit 4/27/16: Added support for comparing one Tristate object to another.


This is called Ternary logic or Three-valued Logic. As other answers suggest, you could either implement a class:

class Ternary:    FALSE = 0    TRUE = 1    UNKNOWN = 2

Myself, I would probably go for your solution (True, False, None) but I understand your concern.


The parallel to the None problem exists with false = 0, true = 1, unknown = 2 (unknown is not actually true either, but will eval to True if you aren't careful).

I came up with a hackish way to get something that at least approximates what you want, I think. It will at least get you something that will evaluate in a trinary fashion in if/else and other boolean eval instances.

class Yes(object):    def __nonzero__(self):        return Trueclass No(object):    def __nonzero__(self):        return Falseclass Unknown(object):    def __nonzero__(self):        raise ValueError('Unknown typed values do not evaluate to True/False.  Try using Ternary.eval().')class Ternary(object):    def __init__(self, yes, no, unknown):        setattr(self, yes, Yes())        setattr(self, no, No())        setattr(self, unknown, Unknown())    @staticmethod    def eval(value, unknown_eval):        if isinstance(value, Unknown):            return unknown_eval        return bool(value)

Usage:

t = Ternary('yes', 'no', 'unknown')# Do stuff to assign ternary value to xif Ternary.eval(x, True):    print 'x is yes or unknown'if Ternary.eval(x, False):    print 'x is yes only'

You could make Yes, No, and Unknown pseudo-singletons which would let you refine eval a little bit. You could still do simple if checks when you know that your value is going to be yes or no, but if you tried to do a straight bool() (ie if x) on Unknown you'd get a TypeError. This would make your code more explicit though, as every time you checked a value of the trinary type, you'd have to define in your code how you wanted unknown to be treated in the context of that conditional, so that would be a plus.

Edit:I thought of an alternative that would require less special handling but less flexible. Alter above thusly:

class Unknown(object):    def __init__(self, eval):        self._eval = eval    def __nonzero__(self):        return self._evalclass Ternary(object):    def __init__(self, yes, no, unknown, unknown_eval):        setattr(self, yes, Yes())        setattr(self, no, No())        setattr(self, unknown, Unknown(unknown_eval))

Usage:

t1 = Ternary('yes', 'no', 'unknown', True)t2 = Ternary('yes', 'no', 'unknown', False)# Do stuff to assign ternary values to x1 and x2if x1:    print 'x is yes or unknown'if x2:    print 'x is yes only'

This has the benefit of allowing nonzero to work as spec calls for in Unknown, but it has the downside of having the eval for Unknown set in stone from instantiation and of no longer allowing Unknown to be a pseudo-singleton.