<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1256">
<meta name="Generator" content="Microsoft Exchange Server">
<!-- converted from text --><style><!-- .EmailQuote { margin-left: 1pt; padding-left: 4pt; border-left: #800000 2px solid; } --></style>
</head>
<body>
<div>
<div>
<div style="font-family:Calibri,sans-serif; font-size:11pt">"Instead of requiring *every* subclass to override all the methods,
<br>
couldn't we require the base classes (like int) to assume that the <br>
signature is unchanged and call type(self), and leave it up to the <br>
subclass to override all the methods *only* if the signature has <br>
changed?"<br>
<br>
I assumed everyone was just saying this point over and over, so I haven't been following the thread closely. This is precisely how inheritance works: subclasses are constrained by the base class. If you want to play, you *must* play by its rules. (Else, use
composition.)<br>
<br>
It's fine for base classes to assume a compatible constructor, and if builtins can do it without hurting performance for the non-subclassed case, I don't see why not.<br>
<br>
Cheers,<br>
Steve<br>
<br>
Top-posted from my Windows Phone</div>
</div>
<div dir="ltr">
<hr>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">From:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt"><a href="mailto:steve@pearwood.info">Steven D'Aprano</a></span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">Sent:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt">ý2/ý14/ý2015 4:24</span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">To:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt"><a href="mailto:python-dev@python.org">python-dev@python.org</a></span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">Subject:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt">Re: [Python-Dev] subclassing builtin data structures</span><br>
<br>
</div>
</div>
<font size="2"><span style="font-size:10pt;">
<div class="PlainText">On Fri, Feb 13, 2015 at 06:03:35PM -0500, Neil Girdhar wrote:<br>
> I personally don't think this is a big enough issue to warrant any changes,<br>
> but I think Serhiy's solution would be the ideal best with one additional<br>
> parameter: the caller's type. Something like<br>
> <br>
> def __make_me__(self, cls, *args, **kwargs)<br>
> <br>
> and the idea is that any time you want to construct a type, instead of<br>
> <br>
> self.__class__(assumed arguments…)<br>
> <br>
> where you are not sure that the derived class' constructor knows the right<br>
> argument types, you do<br>
> <br>
> def SomeCls:<br>
> def some_method(self, ...):<br>
> return self.__make_me__(SomeCls, assumed arguments…)<br>
> <br>
> Now the derived class knows who is asking for a copy.<br>
<br>
What if you wish to return an instance from a classmethod? You don't <br>
have a `self` available.<br>
<br>
class SomeCls:<br>
def __init__(self, x, y, z):<br>
...<br>
@classmethod<br>
def from_spam(cls, spam):<br>
x, y, z = process(spam)<br>
return cls.__make_me__(self, cls, x, y, z) # oops, no self<br>
<br>
<br>
Even if you are calling from an instance method, and self is available, <br>
you cannot assume that the information needed for the subclass <br>
constructor is still available. Perhaps that information is used in the <br>
constructor and then discarded.<br>
<br>
The problem we wish to solve is that when subclassing, methods of some<br>
base class blindly return instances of itself, instead of self's type:<br>
<br>
<br>
py> class MyInt(int):<br>
... pass<br>
...<br>
py> n = MyInt(23)<br>
py> assert isinstance(n, MyInt)<br>
py> assert isinstance(n+1, MyInt)<br>
Traceback (most recent call last):<br>
File "<stdin>", line 1, in ?<br>
AssertionError<br>
<br>
<br>
The means that subclasses often have to override all the parent's <br>
methods, just to ensure the type is correct:<br>
<br>
class MyInt(int):<br>
def __add__(self, other):<br>
o = super().__add__(other)<br>
if o is not NotImplemented:<br>
o = type(self)(o)<br>
return o<br>
<br>
<br>
Something like that, repeated for all the int methods, should work:<br>
<br>
py> n = MyInt(23)<br>
py> type(n+1)<br>
<class '__main__.MyInt'><br>
<br>
<br>
This is tedious and error prone, but at least once it is done, <br>
subclasses of MyInt will Just Work:<br>
<br>
<br>
py> class MyOtherInt(MyInt):<br>
... pass<br>
...<br>
py> a = MyOtherInt(42)<br>
py> type(a + 1000)<br>
<class '__main__.MyOtherInt'><br>
<br>
<br>
(At least, *in general* they will work. See below.)<br>
<br>
So, why not have int's methods use type(self) instead of hard coding <br>
int? The answer is that *some* subclasses might override the <br>
constructor, which would cause the __add__ method to fail:<br>
<br>
# this will fail if the constructor has a different signature<br>
o = type(self)(o)<br>
<br>
<br>
Okay, but changing the constructor signature is quite unusual. Mostly, <br>
people subclass to add new methods or attributes, or to override a <br>
specific method. The dict/defaultdict situation is relatively uncommon.<br>
<br>
Instead of requiring *every* subclass to override all the methods, <br>
couldn't we require the base classes (like int) to assume that the <br>
signature is unchanged and call type(self), and leave it up to the <br>
subclass to override all the methods *only* if the signature has <br>
changed? (Which they probably would have to do anyway.)<br>
<br>
As the MyInt example above shows, or datetime in the standard library, <br>
this actually works fine in practice:<br>
<br>
py> from datetime import datetime<br>
py> class MySpecialDateTime(datetime):<br>
... pass<br>
...<br>
py> t = MySpecialDateTime.today()<br>
py> type(t)<br>
<class '__main__.MySpecialDateTime'><br>
<br>
<br>
Why can't int, str, list, tuple etc. be more like datetime?<br>
<br>
<br>
<br>
-- <br>
Steve<br>
_______________________________________________<br>
Python-Dev mailing list<br>
Python-Dev@python.org<br>
<a href="https://mail.python.org/mailman/listinfo/python-dev">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/steve.dower%40microsoft.com">
https://mail.python.org/mailman/options/python-dev/steve.dower%40microsoft.com</a><br>
</div>
</span></font>
</body>
</html>