missing 'xor' Boolean operator

  • Thread starter Dr. Phillip M. Feldman
  • Start date
P

Paul Rubin

Anthony Tolle said:
def xor(*operands):
if operands:
operands = list(operands)
a = bool(operands.pop(0))
while operands:
b = bool(operands.pop(0))
if a:
if b:
a = False
elif b:
a = True
return a
return False

Among other things, that uses quadratic time! Why do you want to keep
popping items from that list instead of iterating through it anyway?

Anyway, I think you wrote something close to this:

def xor(*operands):
r = False
for x in operands:
r = (r != bool(x))
return r

or in map-reduce style:

from operator import ne
def xor(*operands):
return reduce(ne, map(bool, operands), False)
 
S

Steven D'Aprano

Didn't know that.
So if I resume:
- not 'foo' => False
- 'foo' or 'foo' => 'foo'

I may be missing something, but honestly, Guido must have smoked some
heavy stuff to write such logic, has he ?

No, it's perfectly reasonable, and not at all the product of mind-
altering drugs. Other languages do the same thing.


for x in alist or blist or clist:
print x

will select the first non-empty list and iterate over that.



Every object in Python has a truth value. By convention, we say that
objects are Something or Nothing.

0 None [] '' are examples of Nothing, or false values.

1 2.5 [x, y, z] 'foo' are examples of Something, or true values.

Note the distinction between lower-case "false" and "true" (adjectives),
and title-case False and True (nouns).


`if x: ... else:` branches according to whether x is Something or
Nothing, not whether it is True or False.

The operators `and` and `or` also return Something or Nothing, short-
circuiting as appropriate.
 
S

Steven D'Aprano

De Morgan would turn in his grave.

No he wouldn't. Python isn't Boolean algebra, and there is no requirement
to limit Python's operators to the semantics of Boolean algebra.
 
L

Lino Mastrodomenico

2009/7/16 Hendrik van Rooyen said:
De Morgan would turn in his grave.

If this can make him happier, in Python (not (not a and not b)) *is*
equivalent to bool(a or b). (Modulo crazy things like redefining
"bool" or having a __bool__ with side effects.)

In the first expression you implicitly request a bool because you use
"not", in the second one you do this with an explicit "bool".
 
J

Jean-Michel Pichavant

Nobody said:
So if I resume:
- not 'foo' => False
- 'foo' or 'foo' => 'foo'

I may be missing something, but honestly, Guido must have smoked some
heavy stuff to write such logic, has he ?

Several languages (e.g. Lisp, Bourne shell) behave the same way, i.e. "or"
returns the first element which is considered true while "and" returns the
last element provided that all preceding elements are considered true.

[snip]

Ok then, why "or" does not return True, if the first element is
considered True ? Why returning the element itself. Any reason for that
? Because it's confusing, maybe people used to that logic find it
obvious, but I really don't.

JM
 
A

Anthony Tolle

Among other things, that uses quadratic time!  Why do you want to keep
popping items from that list instead of iterating through it anyway?

Anyway, I think you wrote something close to this:
...

Very true! I didn't think about the problems with pop(). I was using
it as a shortcut for pulling off the first operand. I forgot that if
you start with an initial operand of "False", the result will be the
same (0 xor X = X)

While I'm not sure how useful it would be, here's a version of the
first function that returns one of the operands (ala AND and OR),
except in the case where there is an even number of True elements,
where it returns False:

def xor(*operands):
r, rprime = False, False
for x in operands:
xprime = bool(x)
if rprime:
if xprime:
r, rprime = False, False
else:
r, rprime = x, xprime
return r
xor(0, 0) 0
xor(0, 1) 1
xor(1, 0) 1
xor(1, 1) False
xor(0, 1, 2) False
xor(0, 1, 2, 3) 3
xor(None, [])
[]
 
E

Emile van Sebille

On 7/16/2009 2:06 AM Jean-Michel Pichavant said...
Ok then, why "or" does not return True, if the first element is
considered True ? Why returning the element itself. Any reason for that
? Because it's confusing, maybe people used to that logic find it
obvious, but I really don't.

For example, I sometimes use it to set defaults:

daysInAdvance = int(inputVar) or 25

Emile
 
J

Jean-Michel Pichavant

Emile said:
On 7/16/2009 2:06 AM Jean-Michel Pichavant said...

For example, I sometimes use it to set defaults:

daysInAdvance = int(inputVar) or 25

Emile
Sure this looks like an elegant way to set default values and I will use
this form , but I'm not sure this justifies by itself the trickery.
Python has extended the algebra definition of "or" and "and" top any
type, but it is so unintuitive (I'm no LISP programmer). I think than
using the short-circuiting mechanism of bool operators along with the
python tricks is just error prone and may result in bug difficult to
spot, unless you are very aware of all python boolean mechanisms.

JM
 
E

Emile van Sebille

daysInAdvance = int(inputVar) or 25

I don't get it. That doesn't work right when inputVar == "0".
[/QUOTE]
Aah, but you didn't get to define right. :) For that particular
example 0 is not a valid response.

Emile
 
J

Jean-Michel Pichavant

Emile said:
I don't get it. That doesn't work right when inputVar == "0".
Aah, but you didn't get to define right. :) For that particular
example 0 is not a valid response.

Emile
[/QUOTE]
When I was talking about such error prone form of boolean operations, I
didn't expect to be right so quickly :p
Steven explained the truth notion with the Something/Nothing. "0" is
Something, 0 would be Nothing. I'm not sure it makes sens anyway. I
mean, I could easily argue that the number 0 is something. In the end I
wonder if I shouldn't just acknowledge the python mechanism without
trying to find any intuitive/natural/obvious logic in it, knowing that
sometimes the Truth lies far away from the Evidence.

JM
 
L

Luis Alberto Zarrabeitia Gomez

Quoting Jean-Michel Pichavant said:
When I was talking about such error prone form of boolean operations, I
didn't expect to be right so quickly :p

What do you mean by being "right so quickly", and "error prone" in this context?
I would also ask "Unknown" why he believes that "int(intputVar) or 25" doesn't
work right when inputVar == "0". The only false value that int() may return is
zero, so the "or 25" clause is there only for that case. I can't see then how
you think that is an error.
I'm not sure it makes sens anyway. I
mean, I could easily argue that the number 0 is something. In the end I
wonder if I shouldn't just acknowledge the python mechanism

Then look it another way. The "Empty/Nothing" is just standard practice, there
is nothing in python that forces you to be "false" if you are empty, or true
otherwise. Instead, answer this: why do you need a /boolean/ value? Is there any
case where you need to be certain that the object's type is 'bool'? If you think
the answer is "yes", you may want to get more familiar with the "duck typing"
concept. (That doesn't mean that there are no legitimate cases where duck typing
is inappropriate, but that there are many cases where people, specially if they
come from statically typed languages, may believe that it is inappropriate when
it isn't).

In the python world, one should care more about how an object /behaves/ than
from what clase it came. If something quacks like a duck, then assume that it is
a duck, at least for the quacking part.

Most python objects carry a truth value. Sometimes it feels natural (None is
"false", boolean True and False are "true" and "false", empty containers are
expected to be false, 0 and '' are "false"). Sometimes, it is everything but
natural, but that's a problem with the object's implementation (datetime.time
comes to mind). So, you shouldn't care if you have a bool instance - it should
be enough that it behaves like a bool (though, if you need a bool, you can
always construct one). The "True or False" expression could return Giraffes, as
long as Giraffes behave like the bool "True" in boolean context. If you care
about the class of the result, you can ask for its truth value, and if you don't
care about it, you can just ignore it, and use it as you would use a bool.

And then, if you can return any object as long as it behaves properly, what
would be better to return? A new bool? Why not new Giraffe, if they will have
the same behaviour? Guido chose to return the a value that will say more about
the result of the operation than just a boolean. It acts as a boolean - if you
don't need anything else, treat it as such -, but it will be, whenever is
possible, one of the objects in the sequence, in case you need more info.
without
trying to find any intuitive/natural/obvious logic in it, knowing that
sometimes the Truth lies far away from the Evidence.

Don't do that. Many of python's decisions are very well thought. You may
disagree with them, as I do with some, but they are rarely taken lightly. And
this is one that I find very agreeable and in line with the rest of python's
philosophy.
 
N

Nobody

So if I resume:
- not 'foo' => False
- 'foo' or 'foo' => 'foo'

I may be missing something, but honestly, Guido must have smoked some
heavy stuff to write such logic, has he ?

Several languages (e.g. Lisp, Bourne shell) behave the same way, i.e. "or"
returns the first element which is considered true while "and" returns the
last element provided that all preceding elements are considered true.

[snip]

Ok then, why "or" does not return True, if the first element is
considered True ?

If the first element is true, returning the first element is returning
true.
Why returning the element itself. Any reason for that ?

Why not? Where is the benefit in collapsing all true values to True? You
can convert values to True/False with bool(), but the conversion cannot be
reversed.

It only makes a difference if you are interested in the representation
rather than the value. Do you normally test for equality with "is" or "=="?
 
E

Emile van Sebille

On 7/16/2009 1:29 PM Nobody said...
Ah, but it *is* "equivalent"; it isn't "identical", but that's not the
point.

I'm not sure I'd call it equivalent. A or B returns either unaltered,
and not(not A and not B) always returns a boolean. The equivalent would
be not(not( A or B )).

Emile
 
N

Nobody

I'm not sure I'd call it equivalent.

That depends upon what definition of "equivalent" you are using. Apart
from all manner of vague, informal definitions, there is a formal
definition:

A relation "=" is an equivalence relation if the following hold for all
x, y, and z:

x = x
x = y => y = x
x = y and y = z => x = z

An equivalence relation partitions its domain into equivalence classes,
such that an element is "=" (equivalent) to every element in the same
class, and not "=" to every element in every other class.

This is a lesser notion of equality to "identity", which also requires
that x = y => f(x) = f(y) for all f.

Python's notion of boolean values treats x and y as equivalent if
bool(x) == bool(y).

On this basis, the result of "A or B" is *equivalent* to the result of
"not(not A and not B)". If you use either result as a boolean value (e.g.
the test of an "if" or "while" statement, as an operand to "and" or "or",
etc), the effect is the same. The results aren't *identical*, as there
exist ways to distinguish the two.

As for the utility of this behaviour, returning an actual value rather
than True/False allows the operators to be used as "gates". The term
"gate" in digital logic follows from the axioms:

0 and x = 0
1 and x = x

0 or x = x
1 or x = 1

[0 = False, 1 = True]

If you consider the left operand as the "control" and the right operand as
the "data", the control determines whether or not the data can pass
through the gate to the output (i.e. whether the gate is open or closed).

In Python:

and:
False and 7 => False
False and 0 => False
True and 7 => 7
True and 0 => 0

or:
False or 7 => 7
False or 0 => 0
True or 7 => True
True or 0 => True
 
J

Jean-Michel Pichavant

Luis said:
What do you mean by being "right so quickly", and "error prone" in this context?
I would also ask "Unknown" why he believes that "int(intputVar) or 25" doesn't
work right when inputVar == "0". The only false value that int() may return is
zero, so the "or 25" clause is there only for that case. I can't see then how
you think that is an error.

I was saying that using boolean operators with object instead of boolean
values is error prone, cause no language behaves he same way, and all
behaviors are conventions difficult to figure out without diving deeply
into the documentation (or being explained as it happened to me).

I think the initialization trick is an error, because I don't want
foo(0) to set daysInAdvance to 25. I'll want it to set the attribute to
0, cause 0 is a valid integer. 0 is a valid integer content, None
wouldn't be a valid integer content.


JM
 
S

Steven D'Aprano

Python has extended the algebra definition of "or" and "and" top any
type, but it is so unintuitive (I'm no LISP programmer).

I disagree. The Something/Nothing dichotomy is so intuitive to me that I
would hate to go back to a language that only accepted booleans as
arguments to `if`.

I think than
using the short-circuiting mechanism of bool operators along with the
python tricks is just error prone and may result in bug difficult to
spot, unless you are very aware of all python boolean mechanisms.

In other words, if you don't know how Python behaves, you will make
mistakes.

Of course you will. That applies to *anything* -- if you don't know how
it works, you will make mistakes.

Given three result codes, where 0 means "no error" and an arbitrary non-
zero integer means some error, it is simple and easy to write:

failed = result_1 or result_2 or result_3

The equivalent:

failed = (result_1 != 0) or (result_2 != 0) or (result_3 != 0)
# or if you prefer:
succeeded = (result_1 == 0) and (result_2 == 0) and (result_3 == 0)

are longer and more difficult to read and easier to get wrong. Even worse
are tricks like this:

failed = (result_1 + result_2 + result_3) != 0

This obscures the fact that the result codes are flags and makes it seem
like (flag + flag) is meaningful.
 
L

Luis Zarrabeitia

I was saying that using boolean operators with object instead of boolean
values is error prone, cause no language behaves he same way,

I don't know of many languages that actively promote the duck typing concept,
are as highly dynamic as python, have the "metaclass" concept, treats almost
all language concepts as first class citizens (including functions and
classes), and so on. And don't get me started on assignment (which I consider
very natural, by the way, but apparently most of the popular languages have
pretty unnatural assignments).

It is error prone if you are expecting the result to be a bool instead of just
behaving like one (it is certainly unexpected, but you shouldn't be expecting
to get an instance of certain class anyway, for most of python's operations).
And it is confusing if you are reading the "int(inputVar) or 25" line and
have no idea of what it means (but once you know it, it becomes very
readable, almost plain english).
and all
behaviors are conventions difficult to figure out without diving deeply
into the documentation (or being explained as it happened to me).

That happens with many new concepts. Welcome to python, I guess... if you are
willing to shake some of your expectations from previous programming
languages, you will enjoy it. My [little] experience teaching python tells me
that "duck typing", "non-enforced encapsulation" and "everything is an
object" are the hardest to accept for the C# folk at my faculty, but once
past it, they (the few of them who don't leave the course after that) really
enjoy the language.
I think the initialization trick is an error, because I don't want
foo(0) to set daysInAdvance to 25. I'll want it to set the attribute to
0, cause 0 is a valid integer. 0 is a valid integer content, None
wouldn't be a valid integer content.

Well, that wouldn't be a problem with "or", but with the programmer.

The exact same behaviour could be obtained with

if int(inputValue) == 0:
inputValue = 25

and no "or" involved.

However, using only

inputValue = inputValue or 25

could have been an error if you only wanted 25 in case inputValue is None.

(the "or trick" implies certain trust in that the object you have in hand has
a reasonable definition of truth value).
 
J

Jean-Michel Pichavant

Steven said:
On Thu, 16 Jul 2009 15:53:45 +0200, Jean-Michel Pichavant wrote:


Given three result codes, where 0 means "no error" and an arbitrary non-
zero integer means some error, it is simple and easy to write:

failed = result_1 or result_2 or result_3

The equivalent:

failed = (result_1 != 0) or (result_2 != 0) or (result_3 != 0)
# or if you prefer:
succeeded = (result_1 == 0) and (result_2 == 0) and (result_3 == 0)
[snip]

This is, I guess, where we disagree. I find the second proposal less
error prone, and universally understandable unlike the first one. It may
be verbose, it may look even lame to some people, but in the end this is
perfectly reliable, because you manipulate only False or True within the
boolean operations.

The first form does not clearly show what is the failed criteria. It
just happens by coincidence that in this case the failed criteria
matches the Nothingness of result_1, result_2, result_3. What if results
may be 'OK' or 'KO'.

failed = result_1 or result_2 or result_3
won't work.

failed = (result_1 =='KO') or (result_2 =='KO') or (result_3 =='KO') is
lame but reliable.


JM
 

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
474,201
Messages
2,571,049
Members
47,652
Latest member
Campbellamy

Latest Threads

Top