patternpythonMinor
Python Dictionary Black Magic
Viewed 0 times
dictionarymagicpythonblack
Problem
I am defining a subclass of a the python dictionary object
A single integer
Here are some basic examples
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(se
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(se
Solution
I have a few points I would like to suggest.
Firstly, I would rename the
The next thing I would do is rename your
While, in others places it can inhibit understanding:
Now, in your context it is pretty easy to know what the name is supposed to mean. However, especially in class names, its better to have too many characters than have too few. I would rename your class to something like
My next points touch on your
Python understands the conditional in your if-statement just fine (because
I would use the structure of my first example above in your code to help the statement feel less ambiguous.
The second part of
My final point is in your
Instead of raising the class
Other than these suggestions, your code looks nice.
Firstly, I would rename the
name_to_index function to fit the verb-first function naming convention. Based on your other naming conventions, I would use something like convert_name_to_index.The next thing I would do is rename your
mpCmd class. The Pythonic convention is to use PascalCase instead of camelCase for class names. Also, shortening words can be handy in some places:management --> mgt
character --> charWhile, in others places it can inhibit understanding:
# Is this a comfy cat? A comfy cot? Comfy cut?
ct.description = 'Comfy'Now, in your context it is pretty easy to know what the name is supposed to mean. However, especially in class names, its better to have too many characters than have too few. I would rename your class to something like
CommandMap.My next points touch on your
convert_val function.Python understands the conditional in your if-statement just fine (because
or has a higher precidence than and). However, to the human eye, it can be ambiguous:# Is the grouping this?
if (part1 and part2) or (part3 and part4):
# Or this?
if ((part1 and part2) or part3) and part4:
# Or this?
if (part1 and (part2 or part3)) and part4:I would use the structure of my first example above in your code to help the statement feel less ambiguous.
The second part of
convert_val function that I would change is your assert callable(func) line. The assert keyword is generally used in testing and just feels odd here. Here is how I would structure that section of code:elif isinstance(val, (tuple, list)):
func, indexes = val
indexes = rmap(self._convert_key, indexes)
if not callable(func):
raise TypeError('{} is not callable'.format(func))
return (lambda row: func(*rmap((lambda i: row[i]), indexes)))My final point is in your
convert_key function. If the input is not either an int or a str then you raise TypeError. As you have it now this is what prints:>>>raise TypeError
Traceback (most recent call last):
File "", line 1, in
TypeErrorInstead of raising the class
TypeError, raise an instance of type TypeError with its own message:>>>raise TypeError('Key must be an int or a string.')
Traceback (most recent call last):
File "", line 1, in
TypeError: Key must be an int or a string.Other than these suggestions, your code looks nice.
Code Snippets
management --> mgt
character --> char# Is this a comfy cat? A comfy cot? Comfy cut?
ct.description = 'Comfy'# Is the grouping this?
if (part1 and part2) or (part3 and part4):
# Or this?
if ((part1 and part2) or part3) and part4:
# Or this?
if (part1 and (part2 or part3)) and part4:elif isinstance(val, (tuple, list)):
func, indexes = val
indexes = rmap(self._convert_key, indexes)
if not callable(func):
raise TypeError('{} is not callable'.format(func))
return (lambda row: func(*rmap((lambda i: row[i]), indexes)))>>>raise TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeErrorContext
StackExchange Code Review Q#51247, answer score: 3
Revisions (0)
No revisions yet.