TSmith
TSmith

Reputation: 427

Best/Cleanest way to define constant lists or dictionaries

I'm having trouble creating something which would be synonymous to a C header file (.h).

I have medium sized dictionaries and lists (about 1,000 elements), lengthy enums, and '#defines' (well not really), but I can't find a CLEAN way to organize them all. In C, I would throw them all in a header file and never think again about it, however, in Python that's not possible or so I think.

Current dirty solution: I'm initializing all constant variables at the top of either the module or function (module if multiple functions need it).

How can I cleanly organize constant variables?

Upvotes: 41

Views: 68025

Answers (5)

Mark Z.
Mark Z.

Reputation: 2447

Accepted answer is fine, to take it one step further can use typings when defining your constants; you could just specify Final, or go a step further and provide Final[type] as well, e.g.:

from typing import Final, List
CONSTANT1: Final = 'asd'
CONSTANT_FOO: Final[int] = 123
ADLS_ENVIRONMENTS: Final[List[str]] = ["sandbox", "dev", "uat", "prod"]

# etc.

See https://www.python.org/dev/peps/pep-0591/#the-final-annotation

Upvotes: 4

fkotsian
fkotsian

Reputation: 194

If you want to mess with nested constants and don't like dicts, I came up with this fun solution:

# Recursively transform a dict to instances of the desired class

import json
from collections import namedtuple

class DictTransformer():
    @classmethod
    def constantize(self, d):
        return self.transform(d, klass=namedtuple, klassname='namedtuple')

    @classmethod
    def transform(self, d, klass, klassname):
        return self._from_json(self._to_json(d), klass=klass, klassname=klassname)

    @classmethod
    def _to_json(self, d, access_method='__dict__'):
        return json.dumps(d, default=lambda o: getattr(o, access_method, str(o)))

    @classmethod
    def _from_json(self, jsonstr, klass, klassname):
        return json.loads(jsonstr, object_hook=lambda d: klass(klassname, d.keys())(*d.values()))

Ex:

constants = {
  'A': {
    'B': {
      'C': 'D'
    }
  }
}
CONSTANTS = DictTransformer.transform(d, klass=namedtuple, klassname='namedtuple')
CONSTANTS.A.B.C == 'D'

Pros:

  • handles nested dicts
  • can potentially generate other types of dicts/classes
  • namedtuples provide immutability for constants

Cons:

  • may not respond to .keys and .values if those are not provided on your klass (though you can sometimes mimic with ._fields and list(A.B.C))

Thoughts?

h/t to @hlzr and you guys for the original class idea

Upvotes: 0

tsm
tsm

Reputation: 3658

Make a separate file constants.py, and put all globally-relevant constants in there. Then you can import constants to refer to them as constants.SPAM or do the (questionable) from constants import * to refer to them simply as SPAM or EGGS.

While we're here, note that Python doesn't support constant constants. The convention is just to name them in ALL_CAPS and promise not to mutate them.

Upvotes: 5

Blender
Blender

Reputation: 298432

Usually I do this:

File: constants.py

CONSTANT1 = 'asd'
CONSTANT_FOO = 123
CONSTANT_BAR = [1, 2, 5]

File: your_script.py

from constants import CONSTANT1, CONSTANT_FOO
# or if you want *all* of them
# from constants import *

...

Now your constants are in one file and you can nicely import and use them.

Upvotes: 16

Ned Batchelder
Ned Batchelder

Reputation: 375854

Put your constants into their own module:

# constants.py

RED = 1
BLUE = 2
GREEN = 3

Then import that module and use the constants:

import constants

print "RED is", constants.RED

The constants can be any value you like, I've shown integers here, but lists and dicts would work just the same.

Upvotes: 29

Related Questions