Test None for an object that does not implement ==

G

GZ

Hi,

I run into a weird problem. I have a piece of code that looks like the
following:

f(...., a=None, c=None):
assert (a==None)==(c==None)


The problem is that == is not implemented sometimes for values in a
and c, causing an exception NotImplementedError.

I ended up doing assert (not a)==(not c), but I think this code has
other issues, for example, when a=[] and c=['a'], the assertion will
fail, although a is not None.

So how do I reliably test if a value is None or not?

Thanks,
gz
 
P

Paul Rubin

GZ said:
assert (a==None)==(c==None)...
So how do I reliably test if a value is None or not?

Equality is the wrong comparison to start with. Use "a is None".
 
N

Nobody

I run into a weird problem. I have a piece of code that looks like the
following:

f(...., a=None, c=None):
assert (a==None)==(c==None)


The problem is that == is not implemented sometimes for values in a
and c, causing an exception NotImplementedError.

I have no idea how that can happen. If a.__eq__(None) returns
NotImplemented, the interpreter should flip the test and perform the
equivalent of None.__eq__(a), which will return False.
So how do I reliably test if a value is None or not?

As Paul says, use "is" to check whether a value _is_ None. Checking for
equality is almost certainly the wrong thing to do; nothing should compare
equal to None except for None itself, so "x is None" and "x == None"
shouldn't produce different results unless there's a bug in the comparison
method.
 
L

Lie Ryan

nothing should compare equal to None except for None itself, so "x is None"
and "x == None" shouldn't produce different results unless there's a
bug in the comparison method.

not necessarily, for example:

import random
class OddClass:
def __eq__(self, other):
return [True, False][random.randint(0, 1)]

x = OddClass()
print x == None
print x == None
print x == None
print x == None
print x == None


Now, whether doing something like that is advisable or not, that's a
different question; however nothing in python states that you couldn't
have something that compare equal to None whether there is a bug or not
in the comparison method.
 
R

Roy Smith

Lie Ryan said:
Now, whether doing something like that is advisable or not, that's a
different question; however nothing in python states that you couldn't
have something that compare equal to None whether there is a bug or not
in the comparison method.

Just for fun, I tried playing around with subclassing NoneType and
writing an __eq__ for my subclass. Turns out, you can't do that:

Traceback (most recent call last):
File "./none.py", line 5, in <module>
class Nihil(NoneType):
TypeError: Error when calling the metaclass bases
type 'NoneType' is not an acceptable base type
 
C

Chris Angelico

Just for fun, I tried playing around with subclassing NoneType and
writing an __eq__ for my subclass.  Turns out, you can't do that:

Traceback (most recent call last):
 File "./none.py", line 5, in <module>
   class Nihil(NoneType):
TypeError: Error when calling the metaclass bases
   type 'NoneType' is not an acceptable base type

Yes; unfortunately quite a few Python built-in classes can't be
subclassed. It's an unfortunate fact of implementation, I think,
rather than a deliberate rule.

But then, what would you ever need to subclass None for, other than
toys and testing?

ChrisA
 
S

Steven D'Aprano

On Mon, 26 Dec 2011 00:35:46 +1100, Chris Angelico wrote:

[...]
Yes; unfortunately quite a few Python built-in classes can't be
subclassed.


I can't think of any other un-subclassable classes other than NoneType.
Which ones are you thinking of?
 
C

Chris Angelico

I can't think of any other un-subclassable classes other than NoneType.
Which ones are you thinking of?

I don't remember, but it was mentioned in a thread a little while ago.
Experimentation shows that 'bool' is one of them, though. This may
shed some light:
pass

Traceback (most recent call last):
File "<pyshell#103>", line 1, in <module>
class Foo(type(iter)):
TypeError: type 'builtin_function_or_method' is not an acceptable base type

I think there are certain types that are actually not implemented as
classes, and hence cannot be subclassed. This is almost certainly an
implementation detail though; my testing was done in Py3.2 (on Windows
fwiw).

ChrisA
 
R

Roy Smith

Chris Angelico said:
Yes; unfortunately quite a few Python built-in classes can't be
subclassed. It's an unfortunate fact of implementation, I think,
rather than a deliberate rule.

But then, what would you ever need to subclass None for, other than
toys and testing?

You might be to differentiate between temporary and permanent failures.
Let's say you have a WidgetPool, containing Widgets of various classes.

class WidgetPool:
def get_widget(class_name):
"""Return a Widget of a given class. If there are no such
Widgets available, returns None."""
[...]

You might want to return a None subclass to signify, "No such Widgets
are currently available, but they might be if you try again in a little
while", as opposed to "No such Widgets will ever be available".

If you were designing the interface from scratch, you would probably
represent that with an exception hierarchy. However, if this was an old
interface that you were modifying, this might be a way to return a
richer failure indication for new clients without breaking backwards
compatibility for existing code.

Of course, the existing code would probably be using "is None" tests,
and break anyway. But at least that's a plausible scenario for None
subclasses.
 
C

Chris Angelico

If you were designing the interface from scratch, you would probably
represent that with an exception hierarchy

Or possibly with "returns a False value", giving the option of None
for none available, False for none will ever be available. Of course,
you then have to guarantee that your live return values will always
boolify as True.

ChrisA
 
L

Lie Ryan

Chris Angelico said:
Yes; unfortunately quite a few Python built-in classes can't be
subclassed. It's an unfortunate fact of implementation, I think,
rather than a deliberate rule.

But then, what would you ever need to subclass None for, other than
toys and testing?

You might be to differentiate between temporary and permanent failures.
Let's say you have a WidgetPool, containing Widgets of various classes.

class WidgetPool:
def get_widget(class_name):
"""Return a Widget of a given class. If there are no such
Widgets available, returns None."""
[...]

You might want to return a None subclass to signify, "No such Widgets
are currently available, but they might be if you try again in a little
while", as opposed to "No such Widgets will ever be available".

If you were designing the interface from scratch, you would probably
represent that with an exception hierarchy. However, if this was an old
interface that you were modifying, this might be a way to return a
richer failure indication for new clients without breaking backwards
compatibility for existing code.

Of course, the existing code would probably be using "is None" tests,
and break anyway. But at least that's a plausible scenario for None
subclasses.

That scenario doesn't actually need subclassing if you duck type.
 
D

Devin Jeanpierre

At first glance this looked like it should be a simple boolean "and", but
then I realized that when a and c are both unequal to None, the result would
also be True. This implies the logical approach would be exclusive-or (^).

Among booleans, "!=" is exclusive or and "==" is its negation. I don't
see the point of complicating the formula to use ^.

(related: <= is implication. Which is sad, because the arrow points
the wrong way!)

The issue here is that "== None" is being used instead of "is None",
but I believe that's been covered. Your response doesn't include it,
so maybe it's worth restating.

-- Devin
 
R

Roy Smith

Devin Jeanpierre said:
The issue here is that "== None" is being used instead of "is None",
but I believe that's been covered. Your response doesn't include it,
so maybe it's worth restating.

Which of course leads to a SillyPEP for a new keyword, "are", which
would allow you to write:

instead of the much more verbose
 
S

Steven D'Aprano

<...>

At first glance this looked like it should be a simple boolean "and",
but then I realized that when a and c are both unequal to None, the
result would also be True. This implies the logical approach would be
exclusive-or (^). Try this expression:

not ((a==None) ^ (c==None))

^ is *bitwise* xor, not boolean xor. Python doesn't offer boolean xor
directly, although != comes close.

OTOH, if what you really want is simply to check that both are None (my
first impression), then it's simply:

(a==None) and (c==None)

Replace == with 'is'.
Most of the replies you're getting here seem unnecessarily complicated.

== is a more complicated operator than the 'is' operator. That's why the
is operator is to be preferred when testing for None -- it is guaranteed
to do the right thing, while == is not.
 
I

Ian Kelly

I have no idea how that can happen. If a.__eq__(None) returns
NotImplemented, the interpreter should flip the test and perform the
equivalent of None.__eq__(a), which will return False.

Maybe the class has a misbehaved __eq__ that raises
NotImplementedError directly instead of returning NotImplemented.
 
E

Ethan Furman

Nobody said:
nothing should compare
equal to None except for None itself, so "x is None" and "x == None"
shouldn't produce different results unless there's a bug in the comparison
method.

Why wouldn't you want other types that can compare equal to None? It
could be useful for a Null type to == None.

~Ethan~
 
E

Ethan Furman

GZ said:
Hi,

I run into a weird problem. I have a piece of code that looks like the
following:

f(...., a=None, c=None):
assert (a==None)==(c==None)


Um -- if you don't want a and c being passed in, why put them in the
function signature?

~Ethan~
 
R

Roy Smith

Um -- if you don't want a and c being passed in, why put them in the
function signature?

He wants both or neither to be passed in.[/QUOTE]

assert sum(foo is None for foo in [a, c]) % 2 == 0
 

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,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top