Including Fields in Dictionary

I’m trying to use a data dictionary to house names for fields that return numerical values but it seems only string values are being returned.

I have a data dictionary that looks like this:

dicts= {“BrdTemp_dict”: {“UpperLimit_Parameter”:155, “LowerLimit_Parameter”:156, “Temp_Volt”:“Temp”, “SOH":"SOH.BrdTemp.floatval()”}, “BrdVolt_dict”: {“UpperLimit_Parameter”:143, “LowerLimit_Parameter”:144, “Temp_Volt”:“Volt”, “SOH”:“SOH.BrdVolt.floatval()”}}

SOH represents a field that returns a numerical value and is normally accessed like this: current_board_temp = SOH.BrdTemp.GetFloatVal()

However, I’m trying to make the script more flexible by adding the field names in the dictionary but when I access that using: current_board_temp = dicts[dictionary_name][“SOH”]

current_board_temp returns a string that it seems Python won’t recognize as a field that can be used to get the numerical value.

Is there a way to make: current_board_temp = dicts[dictionary_name][“SOH”] the same as: current_board_temp = SOH.BrdTemp.GetFloatVal() returning a field name instead of a string or can the string be converted?

You’re putting "SOH.BrdTemp.floatval()" in the dict, and that’s a string. Why should Python treat it as anything other than a string?

If you want dicts[dictionary_name]["SOH"] to have the value of SOH.BrdTemp.GetFloatVal(), then just put the value of SOH.BrdTemp.GetFloatVal() in the dict:

dicts= {"BrdTemp_dict": {"UpperLimit_Parameter": 155, "LowerLimit_Parameter": 156, "Temp_Volt": "Temp", "SOH": SOH.BrdTemp.floatval()}, "BrdVolt_dict": {"UpperLimit_Parameter": 143, "LowerLimit_Parameter": 144, "Temp_Volt": "Volt", "SOH": SOH.BrdVolt.floatval()}}

Also, I don’t see the point of the "Temp_Volt" entries. I’d probably go for:

dicts= {"Temp": {"UpperLimit_Parameter": 155, "LowerLimit_Parameter": 156, "SOH": SOH.BrdTemp.floatval()}, "Volt": {"UpperLimit_Parameter": 143, "LowerLimit_Parameter": 144, "SOH": SOH.BrdVolt.floatval()}}

@MRAB is right that there is probably a simpler way to solve your problem, that will just avoid the issue you’re having.

That said, you can also do something like this:

stuff = {
  # string key, integer value
  “key1”: 123,
  # string key, **function** value - no parentheses!
  “key2”: SOH.BrdTemp.floatval,
}

Functions and methods can be assigned names, stored in collections, or be passed to another function, like any other value. So you can store a function without calling it, then call it later!

You do then need to check what the type of the value is, because you have to treat ints and functions differently;

entry = dicts[“some key”]
if callable(entry):
  # calls a function we saved in the dict
  value = entry()
elif isinstance(entry, int):
  # it’s already an int so we don’t need to do anything
  value = entry
elif … # any other type you want to check for

@MRAB and @flyinghyrax thank you both for your replies. The reason I don’t put the value into to the dictionary is because the value isn’t static, isn’t known until the script runs, the script is testing the temperature and voltage limits of a machine and so the current temperature and voltage values are grabbed in real time.

But @MRAB your reply made me think I should be able to grab the value and then add it into the dictionary in real time before the dictionary values are sent to the function that actually uses them so I’m going to try that. If that doesn’t work @flyinghyrax I’ll give your suggestion a try.

Thanks again

I’m not sure I like the idea of a dict where a value can be different each time you read it. It feels like the wrong kind of structure.

You make a good point - it is an unusual way to use a dictionary. It’s not so uncommon to have a dictionary where all the values are functions (a dispatch table), but what I suggested is not a good design pattern.

The shared structure of the two configuration dictionaries suggests a class might be a good fit?

e.g. this:

{ 
  "BrdTemp_dict": {
    "UpperLimit_Parameter": 155, 
    "LowerLimit_Parameter": 156, 
    "Temp_Volt": "Temp", 
    "SOH": SOH.BrdTemp.floatval()
  },
  "BrdVolt_dict": ...
}

Could use a type similar to this:

class BoardMeasure:
  # class "initializer" runs when we create a new instance of this class,
  # also often called a "constructor"
  def __init__(self, source: Callable[[], float], lower_limit: float, upper_limit: float):
    # these are the new objects "attributes"
    self.source = source
    self.lower_limit = lower_limit
    self.upper_limit = upper_limit

  def get_current_value(self) -> float:
    return self.source()

This doesn’t prevent you from using a dictionary for configuration:

config = {
  "BrdTemp": {
    "source": SOH.BrdTemp.floatval,
    "lower_limit": 156,
    "upper_limit": 155,
  },
  # ...
}

# this is a "dict comprehension"
measures = {
  # the '**' takes the inner dict and expands it into parameters to pass to BoardMeasure.__init__
  name: BoardMeasure(**options) for name, options in config.items()
}

But if you’re writing the configuration in a Python file anyway, you can also just write the objects out directly:

config = {
  "BrdTemp": BoardMeasure(source=SOH.BrdTemp.floatval, lower_limit=156, upper_limit=155),
  "BrdVolt": BoardMeasure(source=SOH.BrdVolt.floatval, lower_limit=144, upper_limit=143),
  # ...
}

This is just an idea/example based on limited information[1], but in general it can be very convenient to use classes for the different parts of its configuration.


  1. Good class design has a lot to do with exactly what system you’re trying to model, how you want the different parts to relate to each other, and how you want to use the classes you make. ↩︎