Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using namedtuples as de facto consts -- clever or stupid?

I'm coming from a C# background but now doing a fair amount of scientific computing work in Python 3.x., so I'd like some thoughts on how much my style or accent is "weird" by python standards.

In particular, it galls me to no end that there is no such thing as const in Python. My use case is this: I'm saving *.npz files (numpy serialized dictionaries of data), passing dicts, writing files, etc. and the dictionary keys, file names, etc. need to have a consistent, dependable naming schema.

It's clear that typing the same magic stupid string in 8 places is Wrong.

So in my module root, i have a file I normally call base.py:

import os
from collections import namedtuple
from os import path

# This is the tuple that contains operational constants
RuntimeConstants = namedtuple("Const", " ModelDirectoryLabelName \
                                  DefaultModelOutputDirectoryRoot \
                                  ResultDirectoryNameFormat \
                                  PeripheryOutputFilePrefix \
                                  NCOutputFilePrefix \
                                  SummaryPlotFileName \
                                  PeripheryConfigurationName \
                                  ResourceDirectoryName \
                                  StimulusTemplateName")

runtime_consts = RuntimeConstants(ModelDirectoryLabelName=".model-output-root",
                                  DefaultModelOutputDirectoryRoot="model-output",
                                  ResultDirectoryNameFormat="%d %b %y - %H%M",
                                  PeripheryOutputFilePrefix="periphery-output-",
                                  NCOutputFilePrefix="nc-output-",
                                  PeripheryConfigurationName="simulation-configuration.yaml",
                                  SummaryPlotFileName="summary-plots.pdf",
                                  ResourceDirectoryName="resources",
                                  StimulusTemplateName="default_stimulus.yaml"
                                  )
# This is the path of __this file__, which we can then base location on
rootPath = os.path.dirname(os.path.abspath(__file__))

Tuples are immutable; named tuples have semantically meaningful indicies, and now:

  • I can create multiple dictionaries to pass data on the fly, but know what their keys are
  • I can write files with, and retrieve files with known file names and locations.
  • Refactoring means I only have to modify a magic string in one place.
  • I know where my directories are, even when the module is installed.

In c#, it's pretty normal practice to have one or more Constants class filled with public static const string Foo = "some magic string value";, so that's what I've tried to recreate here.

I currently have 4 such namedtuples in base.py, which seems like it's on the edge of having too many -- but i don't need any more than this. They're all semantically different -- I group constants by useage association.

Is this common-ish practice?

like image 493
gvoysey Avatar asked Mar 29 '16 13:03

gvoysey


2 Answers

In the years since I asked this, i have discovered that the actual answer is attrs.

Specifically rebutting my idea of using namedtuple is this explainer.

I would now express what I wanted originally as frozen slotted classes:

import attrs

@attr.s(frozen=True, slots=True)
class ModelParams:
    fs = attr.ib(default=1000)
    ...
# about as close to immutable as you can get.
model_params = ModelParams()
like image 89
gvoysey Avatar answered Nov 01 '22 21:11

gvoysey


It is not. The standard convention for constants is to just use all-caps names to suggest that the value is constant, and document them as constant.

MODEL_DIRECTORY_LABEL_NAME = ".model-output-root"
DEFAULT_MODEL_OUTPUT_DIRECTORY_ROOT = "model-output"
# etc

The user of your module modifies those values at his own risk.

If the constants are naturally associated with a class, they can be class attributes instead of module-level globals, but it is not common to create a class just to group such values.

like image 35
chepner Avatar answered Nov 01 '22 21:11

chepner