Discussion:
Logic operators with "in" statement
Mr.SpOOn
2009-11-16 14:02:28 UTC
Permalink
Hi,
I'm trying to use logical operators (or, and) with the "in" statement,
but I'm having some problems to understand their behavior.

In [1]: l = ['3', 'no3', 'b3']

In [2]: '3' in l
Out[2]: True

In [3]: '3' and '4' in l
Out[3]: False

In [4]: '3' and 'no3' in l
Out[4]: True

This seems to work as I expected.
------------

In [5]: '3' and 'no3' or '3' and '4' in l
Out[5]: 'no3'

In [6]: ('3' and 'no3') or ('3' and '4') in l
Out[6]: 'no3'

I don't understand these outputs.
---------------

In [7]: (('3' and 'no3') or ('3' and '4')) in l
Out[7]: True

In [10]: (('b3' and '4') or ('3' and 'no3')) in l
Out[10]: False

Here I expected to get True in the second case too, so clearly I don't
really get how they work.

Can you help me?
What I really need is to create a sequence of "if" statements to check
the presence of elements in a list, because some of them are mutually
exclusive, so if for example there are both "3" and "no3" it should
return an error.

Thanks,
Carlo
Mr.SpOOn
2009-11-16 14:08:54 UTC
Permalink
Sorry for replying to myself, but I think I understood why I was wrong.

The correct statement should be something like this:

In [13]: ('b3' and '5') in l or ('3' and 'b3') in l
Out[13]: True
Chris Rebert
2009-11-16 14:30:30 UTC
Permalink
Post by Mr.SpOOn
Sorry for replying to myself, but I think I understood why I was wrong.
In [13]: ('b3' and '5') in l or ('3' and 'b3') in l
Out[13]: True
No, you've just run into another misunderstanding.

Given the expression `X and Y`:
If bool(X) is False, it evaluates to X.
Otherwise, it evaluates to Y.

In other words, the subexpression which ends up determining the truth
of the conjunction is what the conjunction evaluates to. `or` works
similarly.
This allows for tricky tricks like:
foo = possiblyFalse or default # use default if given value is false

Thus, due to the parentheses, your expression is equivalent to:
'5' in l or 'b3' in l
Which I trust is not what you intended.

Again, you need to write separate `in`s for each item you need to
check the membership of:
('b3' in l and '5' in l) or ('3' in l and 'b3' in l)

Cheers,
Chris
--
Python language lawyer at your service
http://blog.rebertia.com
Xavier Ho
2009-11-16 14:30:33 UTC
Permalink
Post by Mr.SpOOn
Sorry for replying to myself, but I think I understood why I was wrong.
In [13]: ('b3' and '5') in l or ('3' and 'b3') in l
Out[13]: True
Carlo, I'm not sure what that achieves. Care to enlighten me?

Cheers,
Xav
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20091117/66f270b4/attachment.html>
exarkun
2009-11-16 14:12:27 UTC
Permalink
Post by Mr.SpOOn
Hi,
I'm trying to use logical operators (or, and) with the "in" statement,
but I'm having some problems to understand their behavior.
"and" and "or" have no particular interaction with "in".
Post by Mr.SpOOn
In [1]: l = ['3', 'no3', 'b3']
In [2]: '3' in l
Out[2]: True
In [3]: '3' and '4' in l
Out[3]: False
In [4]: '3' and 'no3' in l
Out[4]: True
This seems to work as I expected.
What this actually does is '3' and ('no3' in l). So it might have
produced the result you expected, but it didn't work how you expected.
:)

Jean-Paul
Richard Brodie
2009-11-16 14:17:20 UTC
Permalink
"Mr.SpOOn" <mr.spoon21 at gmail.com> wrote in message
Post by Mr.SpOOn
In [13]: ('b3' and '5') in l or ('3' and 'b3') in l
Out[13]: True
For anything more than the simplest cases, you might want use sets.

That might be the correct data type from the start, depending on
whether the ordering is important anywhere.
Chris Rebert
2009-11-16 14:21:10 UTC
Permalink
Post by Mr.SpOOn
Hi,
I'm trying to use logical operators (or, and) with the "in" statement,
but I'm having some problems to understand their behavior.
In [1]: l = ['3', 'no3', 'b3']
In [2]: '3' in l
Out[2]: True
In [3]: '3' and '4' in l
Out[3]: False
In [4]: '3' and 'no3' in l
Out[4]: True
This seems to work as I expected.
No, it doesn't. You are misinterpreting. Membership tests via `in` do
NOT distribute over `and` and `or`.

'3' and '4' in l
is equivalent to:
('3') and ('4' in l)

Recall that non-empty sequences and containers are conventionally True
in Python, so your expression is logically equivalent to:
'4' in l
With '3' not being checked for membership at all.

To check multiple items for membership in a contains, you must write
each `in` test separately and then conjoin then by the appropriate
boolean operator:

'3' in l and '4' in l

Cheers,
Chris
--
http://blog.rebertia.com
Xavier Ho
2009-11-16 14:23:01 UTC
Permalink
Post by Mr.SpOOn
Hi,
I'm trying to use logical operators (or, and) with the "in" statement,
but I'm having some problems to understand their behavior.
Hey Carlo, I think your issue here is mistaking 'in' as a statement. It's
just another logic operator, much like 'and' and 'or'... it's not a
statement in itself. At least, I don't think that's how you'd call 'in'.

You have to remember that Python's logical operators (and, or) works
slightly different than most languages. It doesn't return True or False, if
the things compared aren't boolean values. Instead, it returns the first
thing that makes the decision.

For example a statement such as
Post by Mr.SpOOn
Post by Mr.SpOOn
'3' and '4'
'4'

returns the string literal 4.

1.
Python's AND operator returns the first element if the first one is False;
else, it returns the second element. That's all it does.

Python's OR operator returns the first element if the first one is True;
else, it returns the second element.

Think about it. It's a little strange, but they don't return a pure Boolean
value.

2.
Only the empty string is considered False. Any non-empty strings have True
values.

3.
The proper way of using the in operator is probably such: (There may be a
better way I'm not aware of.)
Post by Mr.SpOOn
Post by Mr.SpOOn
'3' in l and '4' in l
False
Post by Mr.SpOOn
Post by Mr.SpOOn
'3' in l and 'no3' in l
True

AND operator has a higher precedence, so you don't need any brackets here, I
think. But anyway, you have to use it like that. So that's something you'll
have to fix first.
Post by Mr.SpOOn
What I really need is to create a sequence of "if" statements to check
the presence of elements in a list, because some of them are mutually
exclusive, so if for example there are both "3" and "no3" it should
return an error
One idea I can think of is to have a presence count; assuming the input is
non-repeating, you increase this count by 1, starting at 0. If you get
anything above one, return this error you speak of, and that might be a good
start.

Good luck,
Xav
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20091117/83bc4cef/attachment.html>
Chris Rebert
2009-11-16 14:46:48 UTC
Permalink
On Mon, Nov 16, 2009 at 6:23 AM, Xavier Ho <contact at xavierho.com> wrote:
<snip>
Post by Xavier Ho
Post by Mr.SpOOn
'3' in l and 'no3' in l
True
AND operator has a higher precedence, so you don't need any brackets here, I
think. But anyway, you have to use it like that. So that's something you'll
have to fix first.
Er, you mean lower precedence. Higher precedence means it would bind
tighter, thus the expression would mean:
'3' in (l and 'no3') in l
which is certainly incorrect.

Cheers,
Chris
--
http://blog.rebertia.com
Xavier Ho
2009-11-17 00:32:43 UTC
Permalink
Post by Xavier Ho
Post by Xavier Ho
AND operator has a higher precedence, so you don't need any brackets
here, I
Post by Xavier Ho
think. But anyway, you have to use it like that. So that's something
you'll
Post by Xavier Ho
have to fix first.
Er, you mean lower precedence. Higher precedence means it would bind
'3' in (l and 'no3') in l
which is certainly incorrect.
Thanks for the correction, Chris.

Cheers,
Xav
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20091117/374c6b31/attachment.html>
Mr.SpOOn
2009-11-17 12:00:45 UTC
Permalink
Thanks everybody for all the answers and explanations.

In the end maybe it is simpler if I use sets for these tests.

Thanks again.

Tim Chase
2009-11-16 14:38:49 UTC
Permalink
Post by Mr.SpOOn
Here I expected to get True in the second case too, so clearly I don't
really get how they work.
"3" or "4" # true
'3'
Post by Mr.SpOOn
'4' or '3' # true
'4'
Post by Mr.SpOOn
'4' in l # false
False
Post by Mr.SpOOn
'3' or False # true
'3'
Post by Mr.SpOOn
'4' or '42' in l # true: same as "'4' or ('42' in l)"
'4'
Post by Mr.SpOOn
'4' or '42'
'4'
Post by Mr.SpOOn
('4' or '42') in l # true: same as "'4' in l"
'4'

It just happens that sometimes you get unexpected results that
happen to be right because of how Python handles strings/numbers
as booleans and order-of-operations.
Post by Mr.SpOOn
What I really need is to create a sequence of "if" statements to check
the presence of elements in a list, because some of them are mutually
exclusive, so if for example there are both "3" and "no3" it should
return an error.
The "in" operator only checks containment of one item, not
multiples, so you have to manage the checking yourself. This is
fairly easy with the any()/all() functions added in 2.5:

any(i in l for i in ("3", "4"))
all(i in l for i in ("3", "4"))

For more complex containment testing, using sets will be more
efficient and offers a richer family of batch operators
(contains, intersection, union, difference, issubset, issuperset)

-tkc
Loading...