3

I have something which can be in three states

(say) Open, Closed, Running

I will try to model it via one of the two ways,

is_open = Boolean(default=False)
is_running = Boolean(default=False)

and Disallow the state (say) is_running=True, is_open = False in application code.

I can also try

state=Char(choices=("O", "C", "R"))

Is one of the ways better, and is there any better way to do this?

Edit: I am using Python (and Django).
Edit 2: After reading the answers below, I guess I am trying to simulate Enums in Python(Which doesnt have them) in a form which is suitable for persisting to DB

CloudyMarble
  • 36,908
  • 70
  • 97
  • 130
agiliq
  • 7,518
  • 14
  • 54
  • 74
  • Which programming language are you using? The available language structures could make a difference in the preferred choice... – thkala Apr 18 '11 at 13:35

6 Answers6

3

The simplest way to handle this in Python is to use string constants. We did exactly that when adding inspect.getgeneratorstate() to Python 3.2. The possible return values from that function are:

GEN_CREATED = 'GEN_CREATED'
GEN_RUNNING = 'GEN_RUNNING'
GEN_SUSPENDED = 'GEN_SUSPENDED'
GEN_CLOSED = 'GEN_CLOSED'

Creating a simple class as a namespace for the constants is another common option (but the attributes of that class should still be strings rather than integers).

In Python, there is minimal gain in using integers over strings for your constants. You lose a little in comparison speed (but not much, due to hash caching and other string comparison optimisations) and use slightly more memory (but not much, since references are the same size regardless of the type of the target), but vastly simplify debugging (since you don't need to translate integer codes to meaningful state names - you can just display the string values).

Ruli
  • 2,592
  • 12
  • 30
  • 40
ncoghlan
  • 40,168
  • 10
  • 71
  • 80
1

actually you need only two, cause in your case:

closed = ! open

And this depends if only one of the three or evntually more than one can be valid at once, like open and running together? if its only one state allowed at once i would use Enumerations

As this issue is in Python which doesnt support Enums i suggest to take a look at: How can I represent an 'Enum' in Python? All 1st Four answers are interesting but i prefere the 1st and 3rd ones, the one from Alexandru Nedelcu:

class State:
    OPEN=1
    CLOSED=2
    RUNNING=3

Or the answer of Mark Harrison like:

OPEN, CLOSED, RUNNING = range(3)
Community
  • 1
  • 1
CloudyMarble
  • 36,908
  • 70
  • 97
  • 130
  • I *think* that you are wrong: the OP probably has a state machine: Closed->Open->Running. You cannot just drop one of the states. – thkala Apr 18 '11 at 13:36
  • Im not droping anything, but the example he choosed is a abit confuing, consider sockets ! in this case i would guess that running includes open, so im just saying the answer depends on how isolated the states from eachother. For only 1 state at once i suggested Enumerations. – CloudyMarble Apr 18 '11 at 13:43
  • O.D. I guess I am trying to simulate Enums in Python(Which doesnt have them) in a form which is suitable for persisting for persisting to DB – agiliq Apr 18 '11 at 13:45
  • @agiliq I added the Python tag as this is very relevant information, please see this thread: http://stackoverflow.com/questions/36932/whats-the-best-way-to-implement-an-enum-in-python – CloudyMarble Apr 19 '11 at 04:21
0

The elusive "Tri-boolean" value. I've seen this before somewhere.

enum Bool 
{ 
    True, 
    False, 
    FileNotFound 
};

What is Truth? - TheDailyWTF

Ed Manet
  • 3,118
  • 3
  • 20
  • 23
0

+1 for suggestions you probably want an enum. Since python doesn't support them, I'd probably define a class to encapsulate it. That way you can enforce rules on update. As a minimum that'll give you the same behaviour as an enum. If appropriate you can also control legal state changes, e.g.

class Widget:
    def __init__(self, state="Closed"):
        self.state=state

    def open(self):
        if self.state == "Closed":
            self.state = "Open"
        elif self.state == "Open":
            pass
        else:
            # do whatever if trying to open in "Running" state...

    def close(self):
        # etc.

class ThingContainingWidget:
    def __init__(self):
        self.widget=Widget()

    def doSomethingThatAffectsWidget(self):
        self.widget.close()
        # etc.

Depending on the problem, you may need/want to move the state change logic to the enclosing class (e.g. if state behaviour is dependent on other attributes of enclosing class too). But I'd probably leave the basic validation (ensuring value can only ever be Open/Closed/Running) in the Widget class.

Haven't done any django in a while, but can't think of any reason why this wouldn't work.

hth.

sfinnie
  • 9,854
  • 1
  • 38
  • 44
0

This is a good place to make use of Python "properties" - which allow you to bind get and set methods to object attributes in a transparent way.

That way, your methods can control the behavior you want, and your code just have to set True or False for your states:

class Machine(object):
    def __init__(self):
        self.open = False
        self._running = False
        self._closed = False

    def get_running(self):
     return self._running

    def set_running(self, val):
        if not self.open and val:
            raise ValueError("Cannot run if it is closed")
        self._running = True

    running = property(get_running, set_running)

    def get_closed(self):
        return self._closed

    def set_closed(self, val):
        if val:
            self.open = False
            self.running = False
        self._closed = val

    closed = property(get_closed, set_closed)

Pasting this on the python console allows for a quick test drive:

>>> m = Machine()
>>> m.open
False
>>> m.running
False
>>> m.running = True
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 12, in set_running
ValueError: Cannot run if it is closed
>>> m.open = True
>>> m.running = True
>>> m.closed
False
>>> m.closed = True
>>> m.open, m.running
(False, True)
>>>
jsbueno
  • 99,910
  • 10
  • 151
  • 209
-1

Depending on your language, you may be able to use an enum:

enum DoohickyState
{
    Open,
    Closed,
    Running
}
Steve Wellens
  • 20,506
  • 2
  • 28
  • 69