【发布时间】:2013-01-19 16:27:30
【问题描述】:
我需要一个函数,它将 python 的运算符符号或关键字之一作为字符串,连同它的操作数,计算它,并返回结果。像这样:
>>> 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
不能假定字符串是安全的。我会满足于仅映射二元运算符,但如果我能得到所有它们,我会更加高兴。
我当前的实现手动将符号映射到 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)
此解决方案在重载运算符上失败 -- add/concat 和 sub/neg。可以添加检查以检测这些情况并检测类型或计数参数以选择正确的函数名称,但这感觉有点难看。如果我在这里没有更好的主意,这就是我的想法。
困扰我的是python已经这样做了。它已经知道如何将符号映射到运算符函数,但据我所知,该功能并未向程序员公开。似乎 python 中的所有其他内容,一直到the pickling protocol,都暴露给了程序员。那么这是哪里?或者为什么不是?
【问题讨论】:
-
我很确定您实际上不需要担心
add与concat,除非您正在处理 C API(或使用一些实现两者的时髦 C 扩展类型插槽并做不同的事情或他们)。换句话说,除非我弄错了,否则从 Python 中,operator.add(seq1, seq2)应该可以工作,如果没有添加插槽,则调用 concat 插槽,所以你可以使用add。 -
另外,Python 实际上并没有将符号映射到
operator函数;它将符号映射到像__add__这样的dunder 方法(实际上,即使这样也不是很准确,因为有C 扩展槽)。operator模块只是一堆碰巧调用相同 dunder 方法的函数。 -
酷!你是对的,添加序列确实适用于我的解决方案。不过,否定/减法没有那么幸运。是的,我不是说 python 做了和我的代码一样的事情,我的意思是它做了我想做的事情。如果我能连接到那个系统而不是自己动手,那感觉就好了。
-
@Phil:否定是一元运算符,减法是二元运算符。
-1是负 1,1-1是减法。所以算上操作数,你应该没问题! -
@Phil:除了
-somevariable对于不同的类型有不同的含义;对象可以实现hooks for unary operators。-somevariable与somevariable * -1不相同。
标签: python functional-programming idioms