checking if two things do not equal None

S

Steven D'Aprano

And for cases where you have more than one or two things to test for
None-itude, you could use

if all(x is None for x in [a, b, c, d]):
do_something_if_theyre_all_None()

or

if all(x is not None for x in [a, b, c, d]):
do_something_if_no_Nones()

or

if not any(x is None for x in [a, b, c, d]):
do_something_if_no_Nones()

which I find *much* more readable from a maintenance point of view.

With one or two things, I would stick to a regular comparison (skipping
the "not"):

a is None
a is b is None

With three, I would consider either idiom:

a is b is c is None
all(x is None for x in (a, b, c))

but lean towards the use of all(). From four onwards I would definitely
use all(), and of course if there is an arbitrary number of items, I
would definitely use all().
 
R

Roy Smith

Chris Angelico said:
Chained comparisons where you're checking a single variable against
two constants make perfect sense:

2 < x < 5

Chained comparisons where you check a single constant against two
variables don't, so much:

x < 2 < y

To me, chained comparisons make intuitive sense when they're all "<" (or
"<="). I just think back to junior high school algebra class, with the
big number line above the blackboard. Thus,

a < b < c

means if you put a, b, and c on the number line, a is to the left of b,
which is to the left of c. I have no problem extending that to more
than three values:

a < b < c < d < e

still makes intuitive sense. I have no particular problem with

x < 2 < y

because it fits the same pattern. But, if you show me

a != None != b:

my brain just goes into overload. Honestly, I don't even know what that
means. My brain keeps trying to stick a, None, and b on Mrs. Albaum's
number line and keeps walking into the wall. If you (the editorial you)
tell me that my failure to grok that expression means I'm not fluent in
Python, well then, guilty as charged.
 
R

Rustom Mody

I have no particular problem with
x < 2 < y
because it fits the same pattern. But, if you show me
a != None != b:
my brain just goes into overload. Honestly, I don't even know what that
means. My brain keeps trying to stick a, None, and b on Mrs. Albaum's
number line and keeps walking into the wall. If you (the editorial you)
tell me that my failure to grok that expression means I'm not fluent in
Python, well then, guilty as charged.

<Math Terminology>
A relation that is reflexive antisymmetric and transitive is a partial order
Strict order: Irreflexive asymmetric and transitive
Both are strongly related
For general R (partial) S (strict)

S from R
xSy = xRy ∧ x ≠ y
R from S
xRy = xSy ∨ x=y
</Math Terminology>

For both these chained comparisons are natural

!= is not transitive: 2 != 3 and 3 != 2 ⊬ 2 == 2

So for != chained comparisons are not natural (or IMHO appropriate)
 
S

Steven D'Aprano

Chained comparisons where you're checking a single variable against two
constants make perfect sense:

2 < x < 5

Chained comparisons where you check a single constant against two
variables don't, so much:

x < 2 < y

What exactly does that mean, and why is it written that way?

It checks that 2 is strictly bounded between x on the left and y on the
right, i.e. that 2 is inside the open interval x...y. I don't know why
you think that's unclear. But then I do have a maths background and I'm
used to chaining comparisons.

Write it like this:

low = x
high = y
a = 2

low < a < high

Does that make more sense? Well-chosen names are good. The fact that a is
a constant rather than a variable is no big deal:

low < 2 < high

We can
figure out how the interpreter will parse that, but does that correspond
to the programmer's intention?

That applies to just about anything:

(x % 2 == 1) or (x > 0)

What that my intention, or did I intend to write

(x % 2 == 0) and (x < 0)


At some point you just have to accept that, in the absence of clearly
nonsensical code or a contradiction between the documentation and the
behaviour (i.e. a bug), the programmer will have written what she
intended to write.

It'd be more useful but less clear if one of the conditions points the
other way:

x < 2 > y

which checks that they're both less than two,

which is quite different from what you wrote the first time.

but IMO in a less-than-clear way.

That's an understatement. If I saw code chaining comparisons in that
fashion, I would assume the second operator > was a typo.

Chaining less-than and greater than operators should, for clarity, always
be written in a single order. E.g. a <= b < c < d, not a <= b < d > c.

(The second contains a subtle bug too.)
 
S

Steven D'Aprano

I have no particular problem with
x < 2 < y
because it fits the same pattern. But, if you show me
a != None != b:
my brain just goes into overload. Honestly, I don't even know what
that means. My brain keeps trying to stick a, None, and b on Mrs.
Albaum's number line and keeps walking into the wall. If you (the
editorial you) tell me that my failure to grok that expression means
I'm not fluent in Python, well then, guilty as charged.

<Math Terminology> [...]
So for != chained comparisons are not natural (or IMHO appropriate)

I tend to agree they're "not natural", although appropriate is another
thing. The problem is that we tend to read something like:

a != b != c

as "all of a, b and c are unequal", corresponding to:

a == b == c

as "all of a, b and c are equal". But that's not what it means. It means
that a != b and b != c, but it says nothing about a and c. And that was
my mistake. The OP actually got it right in their first post, but
sticking None in the middle to ensure it partakes of both comparisons.

a is not None is not b

Still, that's not easily extended to a third item, this would be wrong:

a is not None is not b is not c

since c only gets compared against b, not None. Better is to factor the
"not" out:

not (a is b is c is None)


which now should be clear: you're testing whether or not *all* of a, b
and c are None. If you prefer:

not all(x is None for x in (a, b, c))


Which is more readable is a matter of personal preference.

I think Johannes got it right: boolean logic is easier to reason about
when there is a minimum of "not"s.
 
C

Chris Angelico

It checks that 2 is strictly bounded between x on the left and y on the
right, i.e. that 2 is inside the open interval x...y. I don't know why
you think that's unclear. But then I do have a maths background and I'm
used to chaining comparisons.

Write it like this:

low = x
high = y
a = 2

low < a < high

Does that make more sense? Well-chosen names are good. The fact that a is
a constant rather than a variable is no big deal:

low < 2 < high

The problem isn't that I can't see what the comparisons are. It makes
very good sense to bound a variable within constants; but you already
know exactly where 2 is on the number line, so asking "Is 2 between
these two variables" seems a bit odd. Maybe it's less so with the
strong mathematical background, but it seems odd to me.
which is quite different from what you wrote the first time.



That's an understatement. If I saw code chaining comparisons in that
fashion, I would assume the second operator > was a typo.

Chaining less-than and greater than operators should, for clarity, always
be written in a single order. E.g. a <= b < c < d, not a <= b < d > c.

(The second contains a subtle bug too.)

Agreed.

ChrisA
 
G

Gregory Ewing

Roy said:
But, if you show me

a != None != b:

my brain just goes into overload.

Chained comparisons get weird with not-equal operators.
If you see

a == b == c

then it implies that a == c, but

a != b != c

does *not* imply that a != c. At least it doesn't in
Python; I've never seen any mathematicians write that, so
I don't know what they would make of it.
 
C

Chris Angelico

I don't feel odd about asking the question “Is 2 between these two
values?â€. It's straightforward and concise. Can you explain better why
you find it odd?

Possibly because the "variable between two constants" is something
I've done often (usually in the more explicit form of "x > min && x <
max" in a language without chained comparisons), usually
bounds-checking some value. I've never had to ask whether a single
constant has two variables, one on either side. But that's just that
I've personally never done it; it doesn't mean nobody does it, by any
means.

ChrisA
 
M

Marko Rauhamaa

Gregory Ewing said:
a != b != c

does *not* imply that a != c. At least it doesn't in Python; I've
never seen any mathematicians write that, so I don't know what they
would make of it.

Any resemblance between mathematics notation and Python is purely
coincidental. I must admit I had missed Python's chained comparisons
until this discussion, but now I looked up the definition:

comparison ::= or_expr ( comp_operator or_expr )*
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"

[...]

Formally, if a, b, c, ..., y, z are expressions and op1, op2, ...,
opN are comparison operators, then a op1 b op2 c ... y opN z is
equivalent to a op1 b and b op2 c and ... y opN z, except that each
expression is evaluated at most once.


That means, in my opinion, that you should feel free to use chaining any
way you see fit. Also, the rule is crystal-clear and easy to grasp:
there's an implicit "and" there.

It's another thing, then, if it was a good idea to include chaining
there in the first place, but I trust the idea was properly vetted and
double checked against possible parsing ambiguities.

Even without chaining "is not" is a bit suspect:
File "<stdin>", line 1
False is not not not 0
^
SyntaxError: invalid syntax



Marko
 
R

Roy Smith

Steven D'Aprano said:
I think Johannes got it right: boolean logic is easier to reason about
when there is a minimum of "not"s.

I used to do a lot of digital logic design. In certain logic families,
it's easier to build a NAND gate than an AND gate (and similarly, NOR is
easier than OR). This leads to lots of inverted logic. Adding to the
confusion, many designs would use "active low" logic, which means a 1
was represented by a low voltage, and a 0 by a high voltage. So, you
quickly end up with gibberish like, "not active low clear nand not
active low enable clock". I'm glad I don't do that stuff any more.
 
M

MRAB

I used to do a lot of digital logic design. In certain logic families,
it's easier to build a NAND gate than an AND gate (and similarly, NOR is
easier than OR). This leads to lots of inverted logic. Adding to the
confusion, many designs would use "active low" logic, which means a 1
was represented by a low voltage, and a 0 by a high voltage. So, you
quickly end up with gibberish like, "not active low clear nand not
active low enable clock". I'm glad I don't do that stuff any more.
When you're building with discrete logic chips, NAND gates are useful
because you can use them as inverters too, which helps to keep the chip
count down.
 
G

Gregory Ewing

Roy said:
Adding to the
confusion, many designs would use "active low" logic, which means a 1
was represented by a low voltage, and a 0 by a high voltage. So, you
quickly end up with gibberish like, "not active low clear nand not
active low enable clock".

There are ways of dealing with that in schematic diagrams.
For exammple, if you have two active-low signals A and B
and want to express "A is active or B is active", you
draw an OR gate symbol with inversion circles on the
inputs. That's equivalent to a NAND gate, but makes the
intention clear.

Schematics drawn that way are much easier to follow than
ones that only use the inverted-output versions of the
symbols.
 
J

Jeremy Sanders

if (a, b) != (None, None):
or
if a != None != b:

Preference? Pros? Cons? Alternatives?

I couldn't see anyone else give this, but I like

if None not in (a, b):
pass

Jeremy
 

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,077
Messages
2,570,566
Members
47,202
Latest member
misc.

Latest Threads

Top