从列表中组合多个逻辑条件
我正在尝试做一件看起来很简单的事情:我有一个列表,里面的元素是逻辑表达式,我想要一个函数把它们合并成一个逻辑表达式,只用一个选择的逻辑运算符(& 或 | 就可以了)。
下面的代码可以做到这一点,不过,我在想是不是有更好的方法来实现这个?我觉得这样写不太像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