__eq__() inconvenience when subclassing set
Gabriel Genellina
gagsl-py2 at yahoo.com.ar
Sun Nov 1 02:13:22 EST 2009
En Fri, 30 Oct 2009 17:55:27 -0300, Jess Austin <jess.austin at gmail.com>
escribió:
> On Oct 29, 10:41 pm, "Gabriel Genellina" <gagsl-... at yahoo.com.ar>
> wrote:
>> We know the last test fails because the == logic fails to recognize
>> mySet (on the right side) as a "more specialized" object than frozenset
>> (on the left side), because set and frozenset don't have a common base
>> type (although they share a lot of implementation)
>>
>> I think the only way would require modifying tp_richcompare of
>> set/frozenset objects, so it is aware of subclasses on the right side.
>> Currently, frozenset() == mySet() effectively ignores the fact that
>> mySet is a subclass of set.
>
> I don't think even that would work. By the time set_richcompare() is
> called (incidentally, it's used for both set and frozenset), it's too
> late. That function is not responsible for calling the subclass's
> method. It does call PyAnySet_Check(), but only to short-circuit
> equality and inequality for non-set objects. I believe that something
> higher-level in the interpreter decides to call the right-side type's
> method because it's a subclass of the left-side type, but I'm not
> familiar enough with the code to know where that happens. It may be
> best not to sully such generalized code with a special case for
> this.
>
> I may do some experiments with bytes, str, and unicode, since that
> seems to be an analogous case. There is a basestring type, but at
> this point I don't know that it really helps with anything.
Looks like in 3.1 this can be done with bytes+str and viceversa, even if
bytes and str don't have a common ancestor (other than object; basestring
doesn't exist in 3.x):
p3> Base = bytes
p3> Other = str
p3>
p3> class Derived(Base):
... def __eq__(self, other):
... print('Derived.__eq__')
... return True
...
p3> Derived()==Base()
Derived.__eq__
True
p3> Base()==Derived()
Derived.__eq__
True
p3> Derived()==Other()
Derived.__eq__
True
p3> Other()==Derived()
Derived.__eq__ # !!!
True
p3> Base.mro()
[<class 'bytes'>, <class 'object'>]
p3> Other.mro()
[<class 'str'>, <class 'object'>]
The same example with set+frozenset (the one you're actually interested
in) doesn't work, unfortunately.
After further analysis, this works for bytes and str because both types
refuse to guess and compare to each other; they return NotImplemented when
the right-side operand is not of the same type. And this gives that other
operand the chance of being called.
set and frozenset, on the other hand, are promiscuous: their
tp_richcompare slot happily accepts any set of any kind, derived or not,
and compares their contents. I think it should be a bit more strict: if
the right hand side is not of the same type, and its tp_richcompare slot
is not the default one, it should return NotImplemented. This way the
other type has a chance to be called.
--
Gabriel Genellina
More information about the Python-list
mailing list