I need a function which takes one of python's operator symbols or keywords as a string, along with its operands, evaluates it, and returns the result. Like this:
>>> string_op('<=', 3, 3)
True
>>> string_op('|', 3, 5)
7
>>> string_op('and', 3, 5)
True
>>> string_op('+', 5, 7)
12
>>> string_op('-', -4)
4
The string cannot be assumed to be safe. I will be satisfied with just mapping the binary operators, but I'd be extra happy if I could get all of them.
My current implementation manually maps the symbols to the functions in the operator module:
import operator
def string_op(op, *args, **kwargs):
"""http://docs.python.org/2/library/operator.html"""
symbol_name_map = {
'<': 'lt',
'<=': 'le',
'==': 'eq',
'!=': 'ne',
'>=': 'ge',
'>': 'gt',
'not': 'not_',
'is': 'is_',
'is not': 'is_not',
'+': 'add', # conflict with concat
'&': 'and_', # (bitwise)
'/': 'div',
'//': 'floordiv',
'~': 'invert',
'%': 'mod',
'*': 'mul',
'|': 'or_', # (bitwise)
'pos': 'pos_',
'**': 'pow',
'-': 'sub', # conflicts with neg
'^': 'xor',
'in': 'contains',
'+=': 'iadd', # conflict with iconcat
'&=': 'iand',
'/=': 'idiv',
'//=': 'ifloordiv',
'<<=': 'ilshift',
'%=': 'imod',
'*=': 'imul',
'|=': 'ior',
'**=': 'ipow',
'>>=': 'irshift',
'-=': 'isub',
'^=': 'ixor',
}
if op in symbol_name_map:
return getattr(operator, symbol_name_map[op])(*args, **kwargs)
else:
return getattr(operator, op)(*args, **kwargs)
This solution fails on the overloaded operators -- add
/concat
and sub
/neg
. Checks could be added to detect those cases and detect types or count arguments to pick the right function name, but that feels a bit ugly. It's what I'll go with if I don't get a better idea here.
The thing that is bugging me is that python already does this. It already knows how to map symbols to operator functions, but so far as I can tell, that functionality is not exposed to the programmer. Seems like everything else in python, right down to the pickling protocol, is exposed to programmers. So where is this? or why isn't it?
See Question&Answers more detail:
os