从列表中组合多个逻辑条件

0 投票
2 回答
860 浏览
提问于 2025-04-18 16:36

我正在尝试做一件看起来很简单的事情:我有一个列表,里面的元素是逻辑表达式,我想要一个函数把它们合并成一个逻辑表达式,只用一个选择的逻辑运算符(& 或 | 就可以了)。

下面的代码可以做到这一点,不过,我在想是不是有更好的方法来实现这个?我觉得这样写不太像Python(我觉得最大的问题是使用了“exec”,有没有人知道怎么不使用它来实现?):

def CombineLogicalExpressions(listofexpressions,operator,outputvariableName):
    ''' return a condition combined from individual ones
    listofexpressions = ["a == 1", "b == 2"]
    operator = "and"
    outputvariableName = 'myVar'
    '''
    #generate list from which the condition can be read:
    logicalconditions = []
    for expression in listofexpressions:
        logicalconditions.append(expression)
        logicalconditions.append(' ' + str(operator) + ' ')
    del logicalconditions[-1] # delete the final operator

    #create string that holds the full expression
    condition = ''
    for a in logicalconditions:
        condition = condition + a   
    condition = 'FinalExpression = (' + condition + ')'

    return condition

FinalExpression = False
a = 1
b = 2

condition = CombineLogicalExpressions(['a == 1', 'b == 2'] ,
                                           'and', 'FinalExpression')
#set FinalBooleanQueryResult to the outcome of executing condition:
exec condition
print FinalExpression

你可能会想,为什么我会把逻辑条件写成字符串。这只是上面代码的简化。在实际情况中,我有一个容器对象'someContainer',它有一个方法'.contains(string)',这个方法会返回一个查询,可以用来从容器中筛选出所有键包含'string'的条目:

 #returns sliced someContainer with keys matching 'string'
 someContainer[someContainer.contains(string)]

现在我想扩展'contains()'的功能,让它能够接受一个字符串列表:

#returns sliced someContainer with keys matching all strings in 'listofkeywords'
containsMultiple(someContainer, listofkeywords)

希望这段内容不会太长或太具体,让大家觉得无聊!我尝试过搜索答案,但让我惊讶的是没有找到任何相关的信息。

2 个回答

2

你可以很简单地使用 any()all() 这两个内置函数来实现这个功能:

class Container(object):
    def __init__(self, items):
        self.items = items

    def contains(self, s):
        return s in self.items


def contains_any(some_container, items):
    return any((some_container.contains(i) for i in items))


def contains_all(some_container, items):
    return all((some_container.contains(i) for i in items))


c = Container(['foo', 'bar'])

print contains_any(c, ['foo', 'qux'])
print contains_all(c, ['foo', 'qux'])
print contains_all(c, ['foo', 'bar'])

输出结果:

True
False
True

any()all() 里面的 (some_container.contains(i) for i in items) 是一种叫做 生成器表达式。它和列表推导式有点像,但它是懒惰的,可能会帮你节省很多次调用 Container.contains() 的次数。

1

其实我找到了一种很不错的解决方案,使用了“reduce”函数和操作符模块。操作符模块可以把一个二元操作符变成一个接受两个参数的函数,比如:

 operator.and_(A,B) equivalent to: (A and B).

Reduce会把一个有两个参数的函数逐个应用到一系列参数上,也就是说:

reduce(function,[A,B,C]) equivalent to: function( A, function(B,C) )

接下来我会给出解决我之前提到的简化问题的代码。特别要注意的是,这个方法对我真正感兴趣的容器也同样有效:

def CombineLogicalExpressionsBetter(listofexpressions,operatorname):
    '''
    use as:
    CombineLogicalExpressionsBetter([a == 1, b == 2, c == 'z'], 'and')
    '''
    import operator

    #make it easier for users to enter operators:
    if operatorname in ('and','and_') :
        operatorname = operator.and_
    elif operatorname in ('or','or_'):
        operatorname = operator.or_
    elif operatorname in ('not','not_'):
        operatorname = operator.not_
    else:
        raise Exception("Please enter a valid operator: 
                    'and', 'or', 'not' ... I got: " + operatorname)


    #combine expression:
    totalexpression = reduce(operatorname, (expression for expression in
                                                       listofexpressions))

    return totalexpression

撰写回答