[Python-Dev] Breaking calls to object.init/new (original) (raw)
Adam Olsen rhamph at gmail.com
Thu Mar 22 05:21:18 CET 2007
- Previous message: [Python-Dev] Breaking calls to object.__init__/__new__
- Next message: [Python-Dev] Breaking calls to object.__init__/__new__
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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 withimportant
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
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
- Previous message: [Python-Dev] Breaking calls to object.__init__/__new__
- Next message: [Python-Dev] Breaking calls to object.__init__/__new__
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]