15

This is mainly a "good python style" question.

I have a module which is using a number of constants that feels should be grouped.

Lets say we have Dogs and cat and each of them have number of legs and favorite food.

Note that

  1. we want to model nothing but those constants about Dogs and Cats
  2. quite probably we'll have more animals in the future.
  3. those constants wont be used outside of the current module.

I thought about the following solutions:


Constants at module level

DOG_NUMBER_OF_LEGS = 4
DOG_FAVOURITE_FOOD = ["Socks", "Meat"]
CAT_NUMBER_OF_LEGS = 4
CAT_FAVOURITE_FOOD = ["Lasagna", "Fish"]

They seem not really grouped, but I think it is the solution I prefer.


Classes as namespaces

class Dog(object):
  NUMBER_OF_LEGS = 4
  DOG_FAVOURITE_FOOD = ["Socks", "Meat"]
class Cat(object):
  NUMBER_OF_LEGS = 4
  FAVOURITE_FOOD = ["Lasagna", "Fish"]

I don't like this solution as we'll have class that we wont use and they can be actually instantiated.


Dictionary of constants

ANIMALS_CONFIG = { 
   "DOG" : { 
     "NUMBER_OF_LEGS" : 4,
     "FAVOURITE_FOOD" : ["Socks", "Meat"]
   },
   "CAT" : { 
     "NUMBER_OF_LEGS" : 4,
     "FAVOURITE_FOOD" : ["Lasagna", "Fish"]
   }
}

I also thought about adding submodules but I dont really want to expose those internal constants


what is the most pythonic way to do it / how would you do it?

Mario Corchero
  • 4,987
  • 5
  • 29
  • 59
  • 2
    You could also have a class `Animal` with `dog` and `cat` instances of that class. – syntonym Apr 22 '15 at 08:49
  • 4
    Or you could also use [named tuples](https://docs.python.org/2.7/library/collections.html#collections.namedtuple). – syntonym Apr 22 '15 at 08:50
  • How will you use them in your code? Will you iterate or select? Where would the selection parameter come from? – Peter Wood Apr 22 '15 at 08:52
  • No iteration nor anything like that. Just calling my constant with the full name. Either DOG_NUMBER_OF_LEGS , ANIMALS_CONFIG["DOG"]["NUMBER_OF_LEGS"] or Dog.NUMBER_OF_LEGS – Mario Corchero Apr 22 '15 at 09:00
  • @MarioA.CorcheroJiménez just to confirm, the **only** behaviour you want from the the objects is to hold those two values and access them by key and/or attribute? – jonrsharpe Apr 22 '15 at 09:35

3 Answers3

12

I would go for a fourth option, preferring a collections.namedtuple:

Animal = namedtuple('Animal', 'number_of_legs favourite_food')

You then create instances like:

DOG = Animal(4, ['Socks', 'Meat'])
CAT = Animal(4, ['Lasagna', 'Fish'])

and access the values externally as:

from animals import CAT

print CAT.number_of_legs

There's really no point having classes if you don't need to create any methods, and I think the form of access above is neater than e.g.:

from animals import animals

print animals['CAT']['number_of_legs']

namedtuples, like vanilla tuples, are immutable, too, so you can't accidentally reassign e.g. CAT.number_of_legs = 2somewhere.

Finally, the namedtuple is a lightweight data structure, which may be important if you're creating lots of animals:

>>> import sys
>>> sys.getsizeof({'number_of_legs': 4, 'favourite_food': ['Lasagna', 'Fish']})
140
>>> from collections import namedtuple
>>> Animal = namedtuple('Animal', 'number_of_legs favourite_food')
>>> sys.getsizeof(Animal(4, ['Lasagna', 'Fish']))
36
jonrsharpe
  • 107,083
  • 22
  • 201
  • 376
4

There's no one-size-fits-all answer, it really depends on how many constants you have to handle, how you use them, if having polymorphism dispatch makes sense or not and the phase of the moon.

Now in your example, since you have two or more sets of constants with a similar structure and meaning, I'd go for the "classes as namespaces" solution.

FWIW preventing a class from being instanciated is not that difficult:

>>> class Foo(object):
...    def __new__(cls):
...       return cls
... 
>>> Foo()
<class '__main__.Foo'>

But documentation should be enough - remember that your "constants" are only constants by convention anyway.

bruno desthuilliers
  • 72,252
  • 6
  • 79
  • 103
1

I'd go for the dictionary approach, as it feels more readable and better organized, but I believe it's a matter of personal preference.

Using classes also doesn't seem like a bad idea if you use a class with only static variables and methods like this and you never instantiate them.

Community
  • 1
  • 1
Davide
  • 303
  • 1
  • 8