I know that Python's assignment operator does not copy objects, i.e. assigning a list to two variables does not copy the list. Therefore changing an element with one of the two variables changes 'both lists':
a = [1, 2, 3]
b = a
print(a, b) # output: [1, 2, 3] [1, 2, 3]
b[0] = 0
print(a, b) # output: [0, 2, 3] [0, 2, 3]
Same goes for dictionaries.
Suppose we want to pass a parameter dictionary with string keys and float
or Callable
values, i.e. params: dict[str, float | Callable] = {'a': lambda t: 0.5, 'b': 0.1}
to an object. The object then creates lambda
expressions for each non-callable value and stores this dictionary. Further, for every key that does not exist in the passed dictionary an element is added with the default value lambda t: 0.0
. The object has a method to access said lambda
expressions as well. The code could look like this:
class Parameters:
params: dict[str, float | int | Callable] = {}
def __init__(self, parameters: dict[str, float | int | Callable]) -> None:
valid_keys = ['a', 'b', 'c']
if len(parameters.keys() - valid_keys):
raise ValueError(f"Invalid key in parameters! Valid keys are: {valid_keys}.")
p = parameters.copy()
# check if keys are callable, fix if not
for key, value in p.items():
if not hasattr(value, '__call__'):
p[key] = lambda t: value
# check if all keys are given, use 0 as default otherwise
for key in valid_keys - p.keys():
p[key] = lambda t: 0
self.params = p
def get_values(self, t: int) -> list:
return [value(t) for value in self.params.values()]
The problem now is as follows: if we pass a dictionary with float values to the contructor, i.e. params = Parameters({'a': 0.5, 'b': 0.1})
, print(params.get_values(1))
produces the output [0.1, 0.1, 0]
instead of [0.5, 0.1, 0]
.
This unintended behavior is due to the first loop in the constructor and Python's assignment operator.
How could I do this in a way that the values are correct?