Possible Duplicate:
What's the best way to implement an 'enum' in Python?
I’m writing a function that, ideally, I’d like to return one of three states: “yes”, “no”, and “don’t know”.
Do any programming languages have a type that has three, and only three states? Like a boolean, but with three states instead of two?
In languages that don’t have such a type (like Python), what’s the best type to represent this?
Currently I think I’ll go with an integer (0
for “no”, 1
for “don’t know” and 2
for “yes”), but maybe there’s a better way? Integers seem a bit “magic number”.
I could return True
, False
or None
, but as None
would evaluate to False
in most comparison contexts it seems a bit ripe for errors.
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 # True
t != False # True
t in (True, False) # True
bool(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.