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 True
class No(object):
def __nonzero__(self):
return False
class 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 x
if 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._eval
class 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 x2
if 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.