I am defining a subclass of a the python dictionary object mpCmd
where every item is converted to a lambda. The intended usage is that every item in the dictionary can be called with a single list argument (row
) that will return a value based on row
. Integers and optionally strings are considered indexes of the row
. When converted to indexes, strings are converted as though they were Excel style column names.
A single integer n
will become lambda row: row[n]
. A tuple with a function and a sequence of indexes transforms as
(func, (n1, n2, n3))
=> lambda row: func(row[n1], row[n2], row[n3])
Here are some basic examples
>>>command = mpCmd({0: 0, 'B': (sum, ([1, 2, 4],)), 'C': 'SPAM'})
>>>command[0](['Idle', 'Palin', 'Cleese', 'Chapman', 'Gilliam', 'Jones']))
'Idle'
>>>command[1]([1, 2, 3, 4, 5])
10
>>>command[2](ANYTHING)
'SPAM'
I have tested it and it works. Any advice appreciated, even if it's just confirming this is black magic.
def rmap(func, sequence):
return [rmap(func, i) if isinstance(i, (tuple, list))
#elif isinstance(i, dict) ???
else func(i)
for i in sequence]
def name_to_index(col_name):
"""Converts Excel Style column name to zero offset index
"""
return reduce((lambda index, char: index*26 + int(char, 36) - 9),
col_name, 0) - 1
class mpCmd(dict):
"""Stores user defined maps and converts them to f(vector) = scalar
Every item stored in mpCmd will be converted to int: (lambda row: some_func)
"""
def __init__(self, map_dict, offset=0,
int_is_index=True, str_is_name=False):
self.offset = offset
self.int_is_index = int_is_index
self.str_is_name = str_is_name
super(mpCmd, self).__init__(self._convert_dict(map_dict))
# Override setters, do no override accessors
def __setitem__(self, key, val):
super(mpCmd, self).__setitem__(*self._convert_item(key, val))
def update(self, other):
super(mpCmd, self).update(self._convert_dict(other))
# Macro functions
def _convert_dict(self, other):
return {self._convert_key(key): self._convert_val(val)
for key, val in other.items()}
def _convert_item(self, key, val):
return self._convert_key(key), self._convert_val(val)
# actual replacement
def _convert_val(self, val):
if (isinstance(val, int) and self.int_is_index
or isinstance(val, str) and self.str_is_name):
return (lambda row, index=self._convert_key(val): row[index])
elif isinstance(val, (tuple, list)):
func, indexes = val
assert callable(func)
indexes = rmap(self._convert_key, indexes)
return (lambda row: func(*rmap((lambda i: row[i]), indexes)))
else:
return (lambda *args, **kwargs: val)
def _convert_key(self, key):
if isinstance(key, str):
return name_to_index(key)
elif isinstance(key, int):
return key - self.offset
else:
raise TypeError