list equal to subclass of list?

R

Roy Smith

I have a vague feeling this may have been discussed a long time ago, but
I can't find the thread, so I'll bring it up again.

I recently observed in the "checking if a list is empty" thread that a
list and a subclass of list can compare equal:

----------------------------
class MyList(list):
"I'm a subclass"

l1 = []
l2 = MyList()

print type(l1), type(l2)
print type(l1) == type(l2)
print l1 == l2
----------------------------

when run, prints:

<type 'list'> <class '__main__.MyList'>
False
True

The docs say:

[http://docs.python.org/library/stdtypes.html]
Objects of different types, except different numeric types and different
string types, never compare equal

[http://docs.python.org/release/2.7/reference/expressions.html#notin]
objects of different types (emphasis)always compare unequal

In the test code above, l1 an l2 are different types, at least in the
sense that type() returns something different for each of them. What's
the intended behavior here? Either the code is wrong or the docs are
wrong.
 
E

Ethan Furman

Roy said:
I recently observed in the "checking if a list is empty" thread that a
list and a subclass of list can compare equal:

----------------------------
class MyList(list):
"I'm a subclass"

l1 = []
l2 = MyList()

print type(l1), type(l2)
print type(l1) == type(l2)
print l1 == l2
----------------------------

when run, prints:

<type 'list'> <class '__main__.MyList'>
False
True

The docs say:

[http://docs.python.org/library/stdtypes.html]
Objects of different types, except different numeric types and different
string types, never compare equal

This part of the documentation is talking about built-in types, which
your MyList is not.

[http://docs.python.org/release/2.7/reference/expressions.html#notin]
objects of different types *always* compare unequal

Should probably have the word 'built-in' precede 'types' here, since
constructed objects can do whatever they have been told to do.
In the test code above, l1 an l2 are different types, at least in the
sense that type() returns something different for each of them.

--> MyList.__mro__
(<class '__main__.MyList'>, <type 'list'>, <type 'object'>)

MyList is a list -- just a more specific kind of list -- as can be seen
from its mro; this is analogous to a square (2 sets of parallel lines
joined at 90 degree angles, both sets being the same length) also being
a rectangle (2 sets of parallel lines joined at 90 degree angles).
What's the intended behavior here? Either the code is wrong or the docs
are wrong.

The code is correct.

~Ethan~

PS
Yes, I know my square/rectangle definitions are incomplete, thanks. ;)
 
E

Ethan Furman

Roy said:
I don't think it's unclear at all. It's very clear. Clearly wrong :)

While it is wrong (it should have 'built-in' precede the word 'types'),
it is not wrong in the way you think -- a subclass *is* a type of its
superclass.

I read that as saying that if you implement __eq__(), you must make sure
that it returns False if self and other have different types (and
likewise, __ne__() should return True for that case).

Your understanding is flawed. If your object does not know how to
compare itself to some other object, it should return NotImplemented --
at that point Python will follow the rules outlined in the docs. By
returning NotImplemented you are allowing the other object a chance to
perform the comparison -- after all, it might know how! :) If the
other object also returns NotImplemented then (drum-roll please) they
won't compare equal.

The same way that it says that obj1.__lt__(obj2) must return a consistent
result for all types of obj1 and obj2.

Where do you see that? I couldn't find it.

The point of being able to write your own rich comparison methods is so
you can control what happens -- there is no "must" about it. This is
Python -- do what you want! :)

~Ethan~

PS
I have a broken sense of humor -- sometimes it works, sometimes it
doesn't. My apologies in advance if my attempt at humor was not funny.
 
T

Thomas 'PointedEars' Lahn

Ethan said:
PS
I have a broken sense of humor -- sometimes it works, sometimes it
doesn't. My apologies in advance if my attempt at humor was not funny.

Now that was very unpythonic. Know where your roots are! :)
 
R

Roy Smith

While it is wrong (it should have 'built-in' precede the word 'types'),
it is not wrong in the way you think -- a subclass *is* a type of its
superclass.

Well, consider this:

class List_A(list):
"A list subclass"

class List_B(list):
"Another list subclass"

a = List_A()
b = List_B()
print a == b

It prints "True". Neither a nor b are a type of the other:

print isinstance(List_A, List_B)
print isinstance(List_B, List_A)

False
False
 
E

Ethan Furman

Roy said:
Well, consider this:

class List_A(list):
"A list subclass"

class List_B(list):
"Another list subclass"

a = List_A()
b = List_B()
print a == b

It prints "True". Neither a nor b are a type of the other:

print isinstance(List_A, List_B)
print isinstance(List_B, List_A)

False
False

Okay, considering:
List_A is a user-defined type.
List_B is a user-defined type.
Both are sub-classes of list.
Corrected documentation (which says 'built-in types' etc, etc) says
nothing about user-defined types not being able to be equal to each other
neither List_A nor List_B have overridden the __eq__ method, so
list.__eq__ will be used...

conclusion:
if they have equal elements in the same order, they will compare
equal since they are, in fact, list's

Do you not get the same conclusion?

~Ethan~
 
S

Steven D'Aprano

MyList is a list -- just a more specific kind of list -- as can be seen
from its mro; this is analogous to a square (2 sets of parallel lines
joined at 90 degree angles, both sets being the same length) also being
a rectangle (2 sets of parallel lines joined at 90 degree angles).

Possibly the worst analogy ever! *wink*

http://en.wikipedia.org/wiki/Circle-ellipse_problem

Also known as the square-rectangle problem.

A better analogy might be, Lassie is a dog, and Flipper is a dolphin, so
they are different types of animal. But both dogs and dolphins are
mammals, so in that sense, Lassie and Flipper are both mammals and
therefore the same type of animal. It depends on what you mean by "type".
 
E

Ethan Furman

Steven said:
Possibly the worst analogy ever! *wink*

http://en.wikipedia.org/wiki/Circle-ellipse_problem

Also known as the square-rectangle problem.

A better analogy might be, Lassie is a dog, and Flipper is a dolphin, so
they are different types of animal. But both dogs and dolphins are
mammals, so in that sense, Lassie and Flipper are both mammals and
therefore the same type of animal. It depends on what you mean by "type".

I thought about using the mammal analogy instead, but geometry seemed
simpler. *sigh* Oh, well, can't win 'em all!

~Ethan~
 
R

Roy Smith

[http://docs.python.org/library/stdtypes.html]
Objects of different types, except different numeric types and different
string types, never compare equal

This part of the documentation is talking about built-in types, which
your MyList is not.

[http://docs.python.org/release/2.7/reference/expressions.html#notin]
objects of different types *always* compare unequal

Should probably have the word 'built-in' precede 'types' here, since
constructed objects can do whatever they have been told to do.[/QUOTE]

Changing the docs (in numerous places) to make it clear that this is
only true of built-in types would indeed resolve the problem. As it
reads now, it's a general statement about ALL types, built-in or
user-defined.
 

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

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top