Oops, I meant to call super if necessary: @classmethod def __make_me_cls__(cls, arg_cls, *args, **kwargs): if arg_cls is C: pass elif arg_cls is D: args, kwargs = modified_args_for_D(args, kwargs) elif arg_cls is E: args, kwargs = modified_args_for_D(args, kwargs) else: return super().__make_me_cls__(arg_cls, args, kwargs)
if cls is C: return C(*args, **kwargs) return cls.__make_me_cls__(C, *args, **kwargs) On Sat, Feb 14, 2015 at 3:15 PM, Neil Girdhar <mistersh...@gmail.com> wrote: > I think the __make_me__ pattern discussed earlier is still the most > generic cooperative solution. Here it is with a classmethod version too: > > class C(D, E): > def some_method(self): > return __make_me__(self, C) > > def __make_me__(self, arg_cls, *args, **kwargs): > if arg_cls is C: > pass > elif issubclass(D, arg_cls): > args, kwargs = modified_args_for_D(args, kwargs) > elif issubclass(E, arg_cls): > args, kwargs = modified_args_for_D(args, kwargs) > else: > raise ValueError > > if self.__class__ == C: > return C(*args, **kwargs) > return self.__make_me__(C, *args, **kwargs) > > @classmethod > def __make_me_cls__(cls, arg_cls, *args, **kwargs): > if arg_cls is C: > pass > elif issubclass(D, arg_cls): > args, kwargs = modified_args_for_D(args, kwargs) > elif issubclass(E, arg_cls): > args, kwargs = modified_args_for_D(args, kwargs) > else: > raise ValueError > > if cls == C: > return C(*args, **kwargs) > return cls.__make_me_cls__(C, *args, **kwargs) > > > On Sat, Feb 14, 2015 at 7:23 AM, Steven D'Aprano <st...@pearwood.info> > wrote: > >> On Fri, Feb 13, 2015 at 06:03:35PM -0500, Neil Girdhar wrote: >> > 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 >> > >> > def __make_me__(self, cls, *args, **kwargs) >> > >> > and the idea is that any time you want to construct a type, instead of >> > >> > self.__class__(assumed arguments…) >> > >> > where you are not sure that the derived class' constructor knows the >> right >> > argument types, you do >> > >> > def SomeCls: >> > def some_method(self, ...): >> > return self.__make_me__(SomeCls, assumed arguments…) >> > >> > Now the derived class knows who is asking for a copy. >> >> What if you wish to return an instance from a classmethod? You don't >> have a `self` available. >> >> class SomeCls: >> def __init__(self, x, y, z): >> ... >> @classmethod >> def from_spam(cls, spam): >> x, y, z = process(spam) >> return cls.__make_me__(self, cls, x, y, z) # oops, no self >> >> >> Even if you are calling from an instance method, and self is available, >> you cannot assume that the information needed for the subclass >> constructor is still available. Perhaps that information is used in the >> constructor and then discarded. >> >> The problem we wish to solve is that when subclassing, methods of some >> base class blindly return instances of itself, instead of self's type: >> >> >> py> class MyInt(int): >> ... pass >> ... >> py> n = MyInt(23) >> py> assert isinstance(n, MyInt) >> py> assert isinstance(n+1, MyInt) >> Traceback (most recent call last): >> File "<stdin>", line 1, in ? >> AssertionError >> >> >> The means that subclasses often have to override all the parent's >> methods, just to ensure the type is correct: >> >> class MyInt(int): >> def __add__(self, other): >> o = super().__add__(other) >> if o is not NotImplemented: >> o = type(self)(o) >> return o >> >> >> Something like that, repeated for all the int methods, should work: >> >> py> n = MyInt(23) >> py> type(n+1) >> <class '__main__.MyInt'> >> >> >> This is tedious and error prone, but at least once it is done, >> subclasses of MyInt will Just Work: >> >> >> py> class MyOtherInt(MyInt): >> ... pass >> ... >> py> a = MyOtherInt(42) >> py> type(a + 1000) >> <class '__main__.MyOtherInt'> >> >> >> (At least, *in general* they will work. See below.) >> >> So, why not have int's methods use type(self) instead of hard coding >> int? The answer is that *some* subclasses might override the >> constructor, which would cause the __add__ method to fail: >> >> # this will fail if the constructor has a different signature >> o = type(self)(o) >> >> >> Okay, but changing the constructor signature is quite unusual. Mostly, >> people subclass to add new methods or attributes, or to override a >> specific method. The dict/defaultdict situation is relatively uncommon. >> >> Instead of requiring *every* subclass to override all the methods, >> couldn't we require the base classes (like int) to assume that the >> signature is unchanged and call type(self), and leave it up to the >> subclass to override all the methods *only* if the signature has >> changed? (Which they probably would have to do anyway.) >> >> As the MyInt example above shows, or datetime in the standard library, >> this actually works fine in practice: >> >> py> from datetime import datetime >> py> class MySpecialDateTime(datetime): >> ... pass >> ... >> py> t = MySpecialDateTime.today() >> py> type(t) >> <class '__main__.MySpecialDateTime'> >> >> >> Why can't int, str, list, tuple etc. be more like datetime? >> >> >> >> -- >> Steve >> _______________________________________________ >> Python-Dev mailing list >> Python-Dev@python.org >> https://mail.python.org/mailman/listinfo/python-dev >> Unsubscribe: >> https://mail.python.org/mailman/options/python-dev/mistersheik%40gmail.com >> > >
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com