1

Say I have an object called Tag, and I have three types of tags as indicated by an instance variable in the following way,

class Tag(object):
    def __init__(self, name, type):
        self.name = name
        self.type = type

t1 = Tag("blue", "cold")
t2 = Tag("red", "warm")
t3 = Tag("black", "hot")

Let's say I only allowed three types: cold, warm, and hot. Would it be better to go checking if it is one of these types like this?

if t1.type == "cold":
    # do something
elif t1.type == "warm":
    # do something else
else t1.type == "hot":
    # do something even elser

Or should I create an enum-like object like the one from this question,

class Type:
    COLD=1
    WARM=2        
    HOT=3

And instead create Tags like this?

t1 = Tag("blue", Type.COLD)

The reason I ask this question is because I heard a lot of processing power goes into comparing strings, and even though these are short 3, 4 letter long words, it is possible that I'd be making tens of thousands of comparisons of these types. Do you think its worth it to go creating enum objects for determining the type of an object as in the example I've shown above? Or is there a better way to do what I'm attempting to do?

Community
  • 1
  • 1
john
  • 2,933
  • 4
  • 25
  • 45
  • 2
    I think that one advantage of your enumerated solution is that it makes it clear what the allowed values are. If you accept arbitrary strings, I would argue that is' easier to make mistakes and harder to document your vocabulary. For example, the `logging` module uses this sort of solution for log levels (e.g., `logging.DEBUG`, `logging.INFO`, and so forth). – larsks Sep 26 '11 at 21:07

4 Answers4

2

It's possible the performance difference may not be significant enough for you to worry about it. You should make a simple test if you're concerned about performance. Use Python's timeit module to test the performance of both cases.

Alex Smith
  • 1,385
  • 10
  • 12
2

I wouldn't be worried about the performance of this unless profiling shows that it indeed is a problem. If you're using an IDE, the enum approach has the benefit of typo-checking.

Davy8
  • 29,246
  • 22
  • 107
  • 172
1

I would go with a combination approach -- have the 'enum' be part of the Tag class, accept a string for initialization, and then convert it. Also, instead of using a bunch of if/else branches, you can use a dict to create a dispatch table.

class Tag(object):
    COLD = 0
    WARM = 1
    HOT = 2
    def __init__(self, name, temp):
        if temp.upper() not in ('COLD', 'WARM', 'HOT'):
            raise ValueError("Invalid temp: %r" % temp)
        self.temp = getattr(self, temp.upper())
        self.name = name
    def process(self):
        func = self.temp_dispatch[self.temp]
        func(self)  # NOTE:  have to pass 'self' explicitly
    def cold(self):
        print('brrr')
    def warm(self):
        print('ahhh')
    def hot(self):
        print('ouch!')
    temp_dispatch = {COLD:cold, WARM:warm, HOT:hot}

tag = Tag('testing', 'Cold')
tag.process()
Ethan Furman
  • 52,296
  • 16
  • 127
  • 201
0

In python, it's frequently advantageous to use a dictionary for dispatch rather than a tower of if/elif/else.

So:

class Tag(object):
    def __init__(self, name, type):
        self.name = name
        self.type = type
        self.dispatch = {"cold":Tag.process_cold, "warm":Tag.process_warm, "hot":Tag.process_hot}

    def process(self):
        self.dispatch[type](self)

    def process_cold(self):
        # do something

    def process_warm(self):
        # do something else

    def process_hot(self):
        # do something even elser

And a small additional bit of code can build the dispatch table automatically:

def dispatchTable( klass, prefix ):
    """
    Given a class and a method prefix string, collect all methods in the class
    that start with the prefix, and return a dict mapping from the part of the 
    method name after the prefix to the method itself.

    e.g. you have a class Machine with methods opcode_foo, opcode_bar.
    create_dispatch_table( Machine, "opcode_" ) 
    yields a dict
    { "foo": Machine.opcode_foo, "bar": Machine.opcode_bar }
    """

    dispatch = {}
    for name, fun in inspect.getmembers( klass, inspect.ismethod ):
        if name.startswith(prefix):
            # print "found %s.%s"%(k.__name__,name)
            dispatch[ name.split(prefix)[1] ] = fun 

    return dispatch   
Russell Borogove
  • 16,687
  • 3
  • 37
  • 45