<div dir="ltr">Unlike a regular method, you would never need to call super since you should know everyone that could be calling you. Typically, when you call super, you have something like this:<div><br></div><div>A < B, C</div><div><br></div><div>B < D</div><div><br></div><div>so you end up with </div><div><br></div><div>mro: A, B, C, D</div><div><br></div><div>And then when A calls super and B calls super it gets C which it doesn't know about.</div><div><br></div><div>But in the case of make_me, it's someone like C who is calling make_me. If it gets a method in B, then that's a straight-up bug. make_me needs to be reimplemented in A as well, and A would never delegate up since other classes in the mro chain (like B) might not know about C.</div><div><br></div><div>Best,</div><div>Neil</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Feb 13, 2015 at 7:00 PM, Isaac Schwabacher <span dir="ltr"><<a href="mailto:ischwabacher@wisc.edu" target="_blank">ischwabacher@wisc.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On 15-02-13, Neil Girdhar wrote:<br>
</span>> I personally don't think this is a big enough issue to warrant any changes, but I think Serhiy's solution would be the ideal best with one additional parameter: the caller's type. Something like<br>
<span class="">><br>
> def __make_me__(self, cls, *args, **kwargs)<br>
><br>
><br>
> and the idea is that any time you want to construct a type, instead of<br>
><br>
><br>
> self.__class__(assumed arguments…)<br>
><br>
><br>
</span>> where you are not sure that the derived class' constructor knows the right argument types, you do<br>
<span class="">><br>
><br>
> def SomeCls:<br>
> def some_method(self, ...):<br>
> return self.__make_me__(SomeCls, assumed arguments…)<br>
><br>
><br>
> Now the derived class knows who is asking for a copy. In the case of defaultdict, for example, he can implement __make_me__ as follows:<br>
><br>
><br>
> def __make_me__(self, cls, *args, **kwargs):<br>
> if cls is dict: return default_dict(self.default_factory, *args, **kwargs)<br>
> return default_dict(*args, **kwargs)<br>
><br>
><br>
> essentially the caller is identifying himself so that the receiver knows how to interpret the arguments.<br>
><br>
><br>
> Best,<br>
><br>
><br>
> Neil<br>
<br>
</span>Such a method necessarily involves explicit switching on classes... ew.<br>
Also, to make this work, a class needs to have a relationship with its superclass's superclasses. So in order for DefaultDict's subclasses not to need to know about dict, it would need to look like this:<br>
<br>
class DefaultDict(dict):<br>
....@classmethod # instance method doesn't make sense here<br>
....def __make_me__(cls, base, *args, **kwargs): # make something like base(*args, **kwargs)<br>
........# when we get here, nothing in cls.__mro__ above DefaultDict knows how to construct an equivalent to base(*args, **kwargs) using its own constructor<br>
........if base is DefaultDict:<br>
............return DefaultDict(*args, **kwargs) # if DefaultDict is the best we can do, do it<br>
........elif base is dict:<br>
............return cls.__make_me__(DefaultDict, None, *args, **kwargs) # subclasses that know about DefaultDict but not dict will intercept this<br>
........else:<br>
............super(DefaultDict, cls).__make_me__(base, *args, **kwargs) # we don't know how to make an equivalent to base.__new__(*args, **kwargs), so keep looking<br>
<br>
I don't even think this is guaranteed to construct an object of class cls corresponding to a base(*args, **kwargs) even if it were possible, since multiple inheritance can screw things up. You might need to have an explicit list of "these are the superclasses whose constructors I can imitate", and have the interpreter find an optimal path for you.<br>
<br>
> On Fri, Feb 13, 2015 at 5:55 PM, Alexander Belopolsky <<a href="mailto:alexander.belopolsky@gmail.com">alexander.belopolsky@gmail.com</a>(javascript:main.compose()> wrote:<br>
<span class="">><br>
> ><br>
> > On Fri, Feb 13, 2015 at 4:44 PM, Neil Girdhar <<a href="mailto:mistersheik@gmail.com">mistersheik@gmail.com</a>(javascript:main.compose()> wrote:<br>
> ><br>
> > > Interesting: <a href="http://stackoverflow.com/questions/5490824/should-constructors-comply-with-the-liskov-substitution-principle" target="_blank">http://stackoverflow.com/questions/5490824/should-constructors-comply-with-the-liskov-substitution-principle</a><br>
> > ><br>
> ><br>
> ><br>
> > Let me humbly conjecture that the people who wrote the top answers have background in less capable languages than Python.<br>
> ><br>
> ><br>
</span>> > Not every language allows you to call self.__class__(). In the languages that don't you can get away with incompatible constructor signatures.<br>
<span class="">> ><br>
> ><br>
> > However, let me try to focus the discussion on a specific issue before we go deep into OOP theory.<br>
> ><br>
> ><br>
</span>> > With python's standard datetime.date we have:<br>
<div class="HOEnZb"><div class="h5">> ><br>
> ><br>
> > >>> from datetime import *<br>
> > >>> class Date(date):<br>
> > ... pass<br>
> > ...<br>
> > >>> Date.today()<br>
> > Date(2015, 2, 13)<br>
> > >>> Date.fromordinal(1)<br>
> > Date(1, 1, 1)<br>
> ><br>
> ><br>
> > Both .today() and .fromordinal(1) will break in a subclass that redefines __new__ as follows:<br>
> ><br>
> ><br>
> > >>> class Date2(date):<br>
> > ... def __new__(cls, ymd):<br>
> > ... return date.__new__(cls, *ymd)<br>
> > ...<br>
> > >>> Date2.today()<br>
> > Traceback (most recent call last):<br>
> > File "<stdin>", line 1, in <module><br>
> > TypeError: __new__() takes 2 positional arguments but 4 were given<br>
> > >>> Date2.fromordinal(1)<br>
> > Traceback (most recent call last):<br>
> > File "<stdin>", line 1, in <module><br>
> > TypeError: __new__() takes 2 positional arguments but 4 were given<br>
> ><br>
> ><br>
> ><br>
> ><br>
> > Why is this acceptable, but we have to sacrifice the convenience of having Date + timedelta<br>
> > return Date to make it work with Date2:<br>
> ><br>
> ><br>
> > >>> Date2((1,1,1)) + timedelta(1)<br>
> > datetime.date(1, 1, 2)<br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
> ><br>
</div></div></blockquote></div><br></div>