A and B but not C in list

C

CM

In Python, is there a recommended way to write conditionals of the
form:

"if A and B but not C or D in my list, do something." ?

I may also have variations on this, like "if A but not B, C, or D".

Do I have to just write out all the if and elifs with all possible
conditions, or is there a handier and more code-maintainable way to
deal with this?

Thanks.
 
C

Corey Richardson

In Python, is there a recommended way to write conditionals of the
form:

"if A and B but not C or D in my list, do something." ?

I may also have variations on this, like "if A but not B, C, or D".

Do I have to just write out all the if and elifs with all possible
conditions, or is there a handier and more code-maintainable way to
deal with this?

Thanks.

if (A in list and B in list) and (C not in list or D not in list):
pass

I'm sure the gurus on this list can come up with something better.
 
C

Chris Rebert

In Python, is there a recommended way to write conditionals of the
form:

"if A and B but not C or D in my list, do something."  ?

I may also have variations on this, like "if A but not B, C, or D".

Do I have to just write out all the if and elifs with all possible
conditions, or is there a handier and more code-maintainable way to
deal with this?

Assuming your conditions all involve membership testing...
Use the built-in any() and all() functions. For your first example:

wanteds = [A, B]
unwanteds = [C, D]
if all(wanted in your_list for wanted in wanteds) and \
not any(unwanted in your_list for unwanted in unwanteds):
do_whatever()

You could pull this out into a separate function if you wish.

Cheers,
Chris
 
S

Steven D'Aprano

It's easier and faster if you convert the lists to sets first:

your_set = set(your_list)

if your_set.issuperset(set([A, B])) and your_set.isdisjoint(set([C,
D])):
...

"Easier" is a close thing. I find this easier to remember and write than
set processing, even if it is a couple of characters longer:

if all(x in your_list for x in (A, B)) and not any(x in your_list for x
in (C, D)):
...

And as for "faster", surely that will depend on the number of elements in
your_list? The conversion from list to set doesn't happen for free, and
it's likely that for small enough lists, that time may exceed any time
savings you would otherwise gain.
 
T

Terry Reedy

In Python, is there a recommended way to write conditionals of the
form:

"if A and B but not C or D in my list, do something." ?

I may also have variations on this, like "if A but not B, C, or D".

Do I have to just write out all the if and elifs with all possible
conditions, or is there a handier and more code-maintainable way to
deal with this?

The straightforward code

if a in L and b in L and c not in L and d not in L

scans the list 4 times. One scan be be done generically as follows:

def req_pro(iterable, required = set(), prohibited = set()):
for item in iterable:
if item in prohibited:
return False
elif item in required:
required.remove(item)
else:
return not required # should now be empty

if req_pro(my_list, {A,B}, {C,D}): ...
# untested, of course..
 
I

Ian Kelly

your_set = set(your_list)

if your_set.issuperset(set([A, B])) and your_set.isdisjoint(set([C, D])):

if your_set.intersection([A, B, C, D]) == set([A, B]):
...

Cheers,
Ian
 
P

Peter Otten

Ian said:
your_set = set(your_list)

if your_set.issuperset(set([A, B])) and your_set.isdisjoint(set([C, D])):

if your_set.intersection([A, B, C, D]) == set([A, B]):
...

You can avoid converting your_list to a set with (using 2.7/3.x notation)

if {A, B, C, D}.intersection(your_list) == {A, B}:
...

The items in your_list still have to be hashable, so the approach is not as
general as

if (all(required in your_list for required in (A, B)) and
not any(forbidden in your_list for forbidden in (C, D))):
...

or similar.

Also, it's not as easy to understand, so don't forget the explaining comment
if you use the set-based approach.

Peter
 
B

Boris Borcic

Terry said:
The straightforward code

if a in L and b in L and c not in L and d not in L

scans the list 4 times.

of course for a single scan one can setify the list and write

S=set(L)
if a in S and b in S and c not in S and d not in S

or even, I guess, something like

{a,b} <= S and not S & {c,d}

also, I suppose that in some settings a,b,c,d could be made to belong to a class
that has defined eg

__nonzero__ = __bool__ = S.__contains__

so that the if statement would become

if a and b and not c and not d

btw, did anybody ask the OP which if any of A,B,C,D otoh and L otoh would vary
fastest ?

Whatever, BB
 
A

Arnaud Delobelle

Terry Reedy said:
The straightforward code

if a in L and b in L and c not in L and d not in L

scans the list 4 times. One scan be be done generically as follows:

def req_pro(iterable, required = set(), prohibited = set()):
for item in iterable:
if item in prohibited:
return False
elif item in required:
required.remove(item)
else:
return not required # should now be empty

if req_pro(my_list, {A,B}, {C,D}): ...
# untested, of course..

But what's better? Four fast list scans or one slow one?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top