按符号访问操作员功能

Phi*_*hil 5 python functional-programming idioms

我需要一个函数,它将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
Run Code Online (Sandbox Code Playgroud)

不能认为该字符串是安全的.我只对映射二元运算符感到满意,但如果能得到所有这些运算符,我会非常高兴.

我当前的实现手动将符号映射到运算符模块中的函数:

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)
Run Code Online (Sandbox Code Playgroud)

此解决方案在重载运算符 - add/ concatsub/ 上失败neg.可以添加检查以检测这些情况并检测类型或计数参数以选择正确的函数名称,但这感觉有点难看.如果我在这里没有更好的想法,那就是我要去的地方.

困扰我的是python 已经做到了这一点.它已经知道如何将符号映射到运算符函数,但据我所知,该函数不会暴露给程序员.看起来像python中的其他一切,直到酸洗协议,都暴露给程序员.那么这是哪里?或者为什么不呢?

aba*_*ert 6

Python不映射符号operator功能.它通过调用特殊dunder方法来解释符号.

例如,当你写作时2 * 3,它不会打电话mul(2, 3); 它调用计算出是否使用一些C代码two.__mul__,three.__rmul__或C型当量(槽nb_multiplysq_repeat均等同于两个__mul____rmul__).您可以从C扩展模块调用相同的代码PyNumber_Multiply(two, three).如果你查看源代码operator.mul,它是一个完全独立的函数,它调用相同的函数PyNumber_Multiply.

那么,有没有从映射*operator.mulPython的揭露.

如果你想以编程方式执行此操作,我能想到的最好的方法是解析operator函数的文档字符串(或者,可能是operator.c源代码).例如:

runary = re.compile(r'Same as (.+)a')
rbinary = re.compile(r'Same as a (.+) b')
unary_ops, binary_ops = {}, {}
funcnames = dir(operator)
for funcname in funcnames:
    if (not funcname.startswith('_') and
        not (funcname.startswith('r') and funcname[1:] in funcnames) and
        not (funcname.startswith('i') and funcname[1:] in funcnames)):
        func = getattr(operator, funcname)
        doc = func.__doc__
        m = runary.search(doc)
        if m:
            unary_ops[m.group(1)] = func
        m = rbinary.search(doc)
        if m:
            binary_ops[m.group(1)] = func
Run Code Online (Sandbox Code Playgroud)

我不认为这会遗漏任何东西,但它确实有一些误报,就像"a + b, for a "作为映射到的运算符operator.concat和映射到callable(的运算符一样operator.isCallable.(确切的设置取决于你的Python版本.)随意调整正则表达式,黑名单等方法等.

但是,如果你真的想编写一个解析器,你可能最好为你的实际语言编写一个解析器,而不是为docstrings编写一个解析器来生成你的语言解析器......

如果您尝试解析的语言是Python的一个子集,Python 确实会公开内部以帮助您.请参阅ast模块了解起点.你可能仍会对类似的东西感到高兴pyparsing,但你至少应该玩ast.例如:

sentinel = object()
def string_op(op, arg1, arg2=sentinel):
    s = '{} {}'.format(op, arg1) if arg2 is sentinel else '{} {} {}'.format(op, arg1, arg2)
    a = ast.parse(s).body
Run Code Online (Sandbox Code Playgroud)

打印a(或更好ast.dump(a)),使用它等等.但是,您仍需要映射_ast.Addoperator.add.但是如果你想要映射到一个真正的Python code对象......那么,它的代码也是可用的.