I have two lists that contain mutually exclusive items - let's pick oil and water based liquids for these lists since these naturally can't mix:
waters = ['tea', 'lemonade', 'juice', 'pepsi']
oils = ['olive oil', 'corn oil', 'peanut oil']
I want to test if foo only contains items in the water list or the oils list but NOT both. such that:
foo = ['tea', 'corn oil'] => FAIL
bar = ['pepsi', 'tea', 'juice'] => PASS
baz = ['olive oil'] => PASS
my attempt so far:
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
needless to say it's ugly but works? how can I do it better?
Define everything as sets (or convert them to sets) and then it's straightforward set/bitwise ops:
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
A quick one-liner might look like:
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))
This would be faster if targets
and conflicts
were sets, since in that case the in
operator would work in constant time. If you can't make the inputs sets, then you can write:
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))
If test
is also a set, then you can take advantage of the overloaded <=
operator that does subset checks and write:
def contains_conflicting_types(test, targets, conflicts):
return not (test <= targets or test <= conflicts)
Given the two sets to compare against
waters = frozenset(['tea', 'lemonade', 'juice', 'pepsi'])
oils = frozenset(['olive oil', 'corn oil', 'peanut oil'])
And a test group
foo = frozenset(['tea', 'corn oil'])
You can determine if the set contains items from exclusively one group (using the XOR operator) by checking if the sets are disjoint (two sets are disjoint if their intersection is the empty set).
foo.isdisjoint(waters) ^ foo.isdisjoint(oils)
For Python 2.5 and older, use:
bool(foo.intersection(waters)) ^ bool(foo.intersection(oils))
Alternatively, if you can remember that &
is the intersection operator when acting on two sets. Because readability counts, if you or other people who are (or will be) maintaining your code aren't sure what the &
character means without looking it up, just use s1.intersection(s2)
.
bool(foo & waters) ^ bool(foo & oils)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.