3

I would like to have a place for my physical constants.

The following answer is already a starting point: How-to import constants in many files

So I have a seperate file called constants.py which I import into my projects.

Now, i would like to save and access additional information:

  • units
  • documentation

The resulting interface should be like:

import constants as c

print c.R
>>> 287.102
print c.R.units
>>> J/(kg K)
print c.R.doc
>>> ideal gas constant

Calculations should use c.R to access the value.

It is basically a class, which behaves like the float class but holds two additional strings: units and documentation. How can this be designed?

Community
  • 1
  • 1
marou
  • 95
  • 1
  • 13
  • i'm pretty sure it's not possible - c.R implies it's float an on the very next call you wan't to access attribute of float, which is simple type... You should consider using c.R() instead or c.R.val which would make it possible – MacHala Jan 07 '17 at 15:15
  • 1
    You should look at [this scipy module](https://docs.scipy.org/doc/scipy/reference/constants.html) – Lucas Jan 07 '17 at 15:15
  • Either use a subclass or use delegation and implement all special methods. Which choice is best depends (though I'd lean towards delegation for the least surprises). – Bakuriu Jan 07 '17 at 15:16

2 Answers2

5

Inheriting from class float, you have to overwrite the __new__-method:

class Constant(float):
    def __new__(cls, value, units, doc):
        self = float.__new__(cls, value)
        self.units = units
        self.doc = doc
        return self

R = Constant(287.102, "J/(kg K)", "deal gas constant")

print R, R * 2
>>> 287.102 574.204
print R.units
>>> J/(kg K)
print R.doc
>>> ideal gas constant
Daniel
  • 42,087
  • 4
  • 55
  • 81
1

I recommend using the json library, which will allow you to store your constant values in a readable and modifiable format.

Using @Daniel's Constant class which inherits from float and adds your custom attributes, you can load all your constants at once into a new Constants object.

You can then get these attributes as c.R to access the value.

Complete file:

#!/usr/bin/env python
import json

class Constant(float):
    def __new__(cls, value):
        self = float.__new__(cls, value["value"])  # KeyError if missing "value"
        self.units = value.get("units", None)
        self.doc = value.get("doc", None)
        return self

class Constants():
    # load the json file into a dictionary of Constant objects
    def __init__(self):
        with open("constants.json") as fh:
            json_object = json.load(fh)

        # create a new dictionary
        self.constants_dict = {}
        for constant in json_object.keys():
            # put each Constant into it
            self.constants_dict[constant] = Constant(json_object[constant])

    # try to get the requested attribute
    def __getattr__(self, name):
        # missing keys are returned None, use self.constants_dict[name]
        # if you want to raise a KeyError instead
        return self.constants_dict.get(name, None)

c = Constants()
print c.R         # 287.102
print c.R.doc     # ideal gas constant
print c.R + 5     # 292.102
print c.F.units   # C mol-1
print c.missing   # None

Example constants.json:

{
    "R": {
        "value": 287.102,
        "units": "J/(kg K)",
        "doc": "ideal gas constant"
    },
    "F": {
        "value": 96485.33,
        "units": "C mol-1",
        "doc": "Faraday contant"
    }
}
ti7
  • 16,375
  • 6
  • 40
  • 68