[Python-Dev] Breaking calls to object.__init__/__new__
Adam Olsen
rhamph at gmail.com
Thu Mar 22 05:23:27 CET 2007
On 3/21/07, Adam Olsen <rhamph at gmail.com> wrote:
> On 3/21/07, Jean-Paul Calderone <exarkun at divmod.com> wrote:
> > On Wed, 21 Mar 2007 15:45:16 -0700, Guido van Rossum <guido at python.org> wrote:
> > >See python.org/sf/1683368. I'd like to invite opinions on whether it's
> > >worth breaking an unknown amount of user code in 2.6 for the sake of
> > >stricter argument checking for object.__init__ and object.__new__. I
> > >think it probably isn't; but the strict version could be added to 3.0
> > >and a warning issued in 2.6 in -Wpy3k mode. Alternatively, we could
> > >introduce the stricter code in 2.6, fix the stdlib modules that it
> > >breaks, and hope for the best. Opinions?
> > >
> >
> > Perhaps I misunderstand the patch, but it would appear to break not just
> > some inadvisable uses of super(), but an actual core feature of super().
> > Maybe someone can set me right. Is this correct?
> >
> > class Base(object):
> > def __init__(self, important):
> > # Don't upcall with `important` because object is the base
> > # class and its __init__ doesn't care (or won't accept) it
> > super(Base, self).__init__()
> > self.a = important
> >
> > If so, what are the implications for this?
> >
> > class Other(object):
> > def __init__(self, important):
> > # Don't upcall with `important` because object is the base
> > # class and its __init__ doesn't care (or won't accept) it
> > super(Other, self).__init__()
> > self.b = important
> >
> > class Derived(Base, Other):
> > pass
> >
> >
> > (A similar example could be given where Base and Other take differently
> > named arguments with nothing to do with each other. The end result is
> > the same either way, I think.)
>
> The common name is actually critical. Your argument names are
> essentially a shared namespace, just like that on the object itself,
> and they're both modifying it on the assumption of being the only
> thing that does so.
>
> There's two ways to fix your example. First, adding a common base
> class which is the "owner" of that name:
>
> class Owner(object):
> def __init__(self, important, **kwargs):
> super(Owner, self).__init__(**kwargs) # important is skipped
>
> class Left(Owner):
> def __init__(self, important, **kwargs):
> super(Left, self).__init__(important=important, **kwargs)
>
> class Right(Owner):
> def __init__(self, important, **kwargs):
> super(Right, self).__init__(important=important, **kwargs)
>
> class Derived(Left, Right):
> pass
>
> >>> Derived("hi")
>
>
> The other is to rename the argument, removing the namespace conflict:
>
> class Left(object):
> def __init__(self, oranges, **kwargs):
> super(Left, self).__init__(oranges=oranges, **kwargs)
>
> class Right(object):
> def __init__(self, apples, **kwargs):
> super(Right, self).__init__(apples=apples, **kwargs)
>
> class Derived(Left, Right):
> pass
Hmm, where's that "undo post" button...
That should be:
class Left(object):
def __init__(self, oranges, **kwargs):
super(Left, self).__init__(**kwargs)
class Right(object):
def __init__(self, apples, **kwargs):
super(Right, self).__init__(**kwargs)
class Derived(Left, Right):
pass
And I would have gotten an error when I tested it had I been using the
strict __init__.
>
> >>> Derived(apples=3, oranges=8)
>
> In this second version you could clean up Derived's interface by
> adding either "def __init__(self, apples, oranges, **kwargs)" and
> passing them both explicitly, or by adding "def __init__(self, *,
> **kwargs)" and requiring they by given to you by name. Either way
> you're completely safe.
>
>
> >
> > I think I understand the desire to pull keyword arguments out at each
> > step of the upcalling process, but I don't see how it can work, since
> > "up" calling isn't always what's going on - given a diamond, there's
> > arbitrary side-calling, so for cooperation to work every method has to
> > pass on every argument, so object.__init__ has to take arbitrary args,
> > since no one knows when their "up" call will actually hit object.
> >
> > Since without diamonds, naive "by-name" upcalling works, I assume that
> > super() is actually intended to be used with diamonds, so this seems
> > relevant.
> >
> > I hope I've just overlooked something. Writing this email feels very
> > strange.
>
> super() has always felt strange to me. Now, with PEP 3102 and the
> strict __init__, not so much.
>
> --
> Adam Olsen, aka Rhamphoryncus
>
--
Adam Olsen, aka Rhamphoryncus
More information about the Python-Dev
mailing list