在Python中,如何检查一组项目是否仅属于两个列表中的一个?
我有两个列表,里面的东西是互不相容的——我们可以用油和水来举例,因为这两种东西是不能混合在一起的:
waters = ['tea', 'lemonade', 'juice', 'pepsi']
oils = ['olive oil', 'corn oil', 'peanut oil']
我想检查一下,foo这个列表里是否只包含水列表或油列表里的东西,但不能同时包含这两种。也就是说:
foo = ['tea', 'corn oil'] => FAIL
bar = ['pepsi', 'tea', 'juice'] => PASS
baz = ['olive oil'] => PASS
我到目前为止的尝试:
def contains_conflicting_types(test, targets, conflicts):
intarget = False
inconflict = False
for t in test:
if t in targets:
intarget = True
if t in conflicts:
inconflict = True
if intarget and inconflict:
return True
return False
#Usage:
contains_conflicting_types(['A','B'], ['A','1'], ['B','2']) #returns True A and B are in conflict
不用说,这个方法看起来很糟糕,但能用?我该怎么做得更好呢?
3 个回答
2
给定要比较的两个集合
waters = frozenset(['tea', 'lemonade', 'juice', 'pepsi'])
oils = frozenset(['olive oil', 'corn oil', 'peanut oil'])
还有一个测试组
foo = frozenset(['tea', 'corn oil'])
你可以通过检查这两个集合是否是不相交的(也就是说它们的交集是空的),来判断这个集合是否只包含来自一个组的项目(使用异或运算符)。
foo.isdisjoint(waters) ^ foo.isdisjoint(oils)
对于Python 2.5及更早版本,可以使用:
bool(foo.intersection(waters)) ^ bool(foo.intersection(oils))
另外,如果你记得&
是两个集合的交集运算符。因为可读性很重要,如果你或者其他维护你代码的人不确定&
这个符号的意思,最好用s1.intersection(s2)
来表示。
bool(foo & waters) ^ bool(foo & oils)
2
一个简单的一行代码可能看起来像这样:
def contains_conflicting_types(test, targets, conflicts):
return not(all(t in targets for t in test) or all(t in conflicts for t in test))
如果把 targets
和 conflicts
变成集合(sets),那么这个操作会更快,因为在这种情况下,使用 in
这个操作符的速度是恒定的。如果你不能把输入变成集合,那么你可以这样写:
def contains_conflicting_types(test, targets, conflicts):
targets, conflicts = set(targets), set(conflicts)
return not(all(t in targets for t in test) or all(t in conflicts for t in test))
如果 test
也是一个集合,那么你可以利用重载的 <=
操作符来检查子集,代码可以写成:
def contains_conflicting_types(test, targets, conflicts):
return not (test <= targets or test <= conflicts)
7
把所有东西都定义为集合(或者把它们转换成集合),这样就可以很简单地进行集合运算或者位运算了:
bool(oils & foo) ^ bool(waters & foo)
Out[19]: False
bool(oils & bar) ^ bool(waters & bar)
Out[20]: True
bool(oils & baz) ^ bool(waters & baz)
Out[21]: True