Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #32958 > unrolled thread
| Started by | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| First post | 2012-11-08 16:55 +0100 |
| Last post | 2012-11-12 14:48 +0100 |
| Articles | 8 — 4 participants |
Back to article view | Back to comp.lang.python
int.__init__ incompatible in Python 3.3 Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-11-08 16:55 +0100
Re: int.__init__ incompatible in Python 3.3 Ian Kelly <ian.g.kelly@gmail.com> - 2012-11-08 10:13 -0700
Re: int.__init__ incompatible in Python 3.3 Terry Reedy <tjreedy@udel.edu> - 2012-11-08 15:29 -0500
Re: int.__init__ incompatible in Python 3.3 Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-11-09 08:56 +0100
Re: int.__init__ incompatible in Python 3.3 Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-11-09 11:37 +0000
Re: int.__init__ incompatible in Python 3.3 Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-11-09 13:52 +0100
Re: int.__init__ incompatible in Python 3.3 Ian Kelly <ian.g.kelly@gmail.com> - 2012-11-09 09:30 -0700
Re: int.__init__ incompatible in Python 3.3 Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2012-11-12 14:48 +0100
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-11-08 16:55 +0100 |
| Subject | int.__init__ incompatible in Python 3.3 |
| Message-ID | <481tm9-k6i.ln1@satorlaser.homedns.org> |
Hi!
Preparing for an upgrade from 2.7 to 3, I stumbled across an
incompatibility between 2.7 and 3.2 on one hand and 3.3 on the other:
class X(int):
def __init__(self, value):
super(X, self).__init__(value)
X(42)
On 2.7 and 3.2, the above code works. On 3.3, it gives me a "TypeError:
object.__init__() takes no parameters". To some extent, this makes sense
to me, because the int subobject is not initialized in __init__ but in
__new__. As a workaround, I can simple drop the parameter from the call.
However, breaking backward compatibility is another issue, so I wonder
if that should be considered as a bug.
Bug? Feature? Other suggestions?
Uli
[toc] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2012-11-08 10:13 -0700 |
| Message-ID | <mailman.3453.1352394822.27098.python-list@python.org> |
| In reply to | #32958 |
On Thu, Nov 8, 2012 at 8:55 AM, Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> wrote: > Hi! > > Preparing for an upgrade from 2.7 to 3, I stumbled across an incompatibility > between 2.7 and 3.2 on one hand and 3.3 on the other: > > class X(int): > def __init__(self, value): > super(X, self).__init__(value) > X(42) > > On 2.7 and 3.2, the above code works. On 3.3, it gives me a "TypeError: > object.__init__() takes no parameters". To some extent, this makes sense to > me, because the int subobject is not initialized in __init__ but in __new__. > As a workaround, I can simple drop the parameter from the call. However, > breaking backward compatibility is another issue, so I wonder if that should > be considered as a bug. > > Bug? Feature? Other suggestions? A similar change was made to object.__init__ in 2.6, so this could just be bringing the behavior of int into line with object. There's nothing about it in the whatsnew document, though. I say open a bug report and let the devs sort it out.
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2012-11-08 15:29 -0500 |
| Message-ID | <mailman.3463.1352406597.27098.python-list@python.org> |
| In reply to | #32958 |
On 11/8/2012 12:13 PM, Ian Kelly wrote: > On Thu, Nov 8, 2012 at 8:55 AM, Ulrich Eckhardt > <ulrich.eckhardt@dominolaser.com> wrote: >> Preparing for an upgrade from 2.7 to 3, I stumbled across an incompatibility >> between 2.7 and 3.2 on one hand and 3.3 on the other: >> >> class X(int): >> def __init__(self, value): >> super(X, self).__init__(value) This is a bug. Subclasses of immutables should not define __init__. >>> int.__init__ is object.__init__ True object.__init__(self) is a dummy placeholder function that takes no args and does nothing. >> X(42) >> >> On 2.7 and 3.2, the above code works. That is a bug. It is documented that calling with the wrong number of args is an error. >> On 3.3, it gives me a "TypeError: object.__init__() takes no parameters". >> To some extent, this makes sense to >> me, because the int subobject is not initialized in __init__ but in __new__. >> As a workaround, I can simple drop the parameter from the call. Just drop the do-nothing call. >> breaking backward compatibility is another issue, so I wonder if that should >> be considered as a bug. Every bug fix breaks backward compatibility with code that depends on the bug. Such breakage is not a bug, but, as in this case, some fixes are not put in bugfix releases because of such breakage. >> Bug? Feature? Other suggestions? Intentional bugfix. http://bugs.python.org/issue1683368 There was additional discussion on pydev or python-ideas lists before the final commit. This fix was not back-ported to 2.7 or 3.2. > A similar change was made to object.__init__ in 2.6, so this could > just be bringing the behavior of int into line with object. There's > nothing about it in the whatsnew document, though. What's New is a summary of *new* features. It does not list bug fixes. At the top it says " For full details, see the Misc/NEWS file." The last patch on the issue added this entry. ''' Core and Builtins ----------------- - Issue #1683368: object.__new__ and object.__init__ raise a TypeError if they are passed arguments and their complementary method is not overridden. ''' > I say open a bug report and let the devs sort it out. Please do not. The current situation is the result of 'sorting it out' over several years. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-11-09 08:56 +0100 |
| Message-ID | <6ipum9-oim.ln1@satorlaser.homedns.org> |
| In reply to | #32978 |
Am 08.11.2012 21:29, schrieb Terry Reedy:
> On Thu, Nov 8, 2012 at 8:55 AM, Ulrich Eckhardt
> <ulrich.eckhardt@dominolaser.com> wrote:
>>> On 3.3, it gives me a "TypeError: object.__init__() takes no
>>> parameters". To some extent, this makes sense to me, because the
>>> int subobject is not initialized in __init__ but in __new__. As a
>>> workaround, I can simple drop the parameter from the call.
>
> Just drop the do-nothing call.
Wait: Which call exactly?
Do you suggest that I shouldn't override __init__? The problem is that I
need to attach additional info to the int and that I just pass this to
the class on contstruction.
Or, do you suggest I don't call super().__init__()? That would seem
unclean to me.
Just for your info, the class mimics a C enumeration, roughly it looks
like this:
class Foo(int):
def __init__(self, value, name):
super(Foo, self).__init__(value)
self.name = name
def __str__(self):
return self.name
Foo.AVALUE = Foo(1, 'AVALUE')
Foo.BVALUE = Foo(2, 'BVALUE')
Note that even though I derive from an immutable class, the resulting
class is not formally immutable. Maybe exactly that is the thing that
the developers did not want me to do? I didn't understand all the
implications in the bug ticket you quoted, to be honest.
Thank you for your time!
Uli
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-11-09 11:37 +0000 |
| Message-ID | <509ceb08$0$29980$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #33003 |
On Fri, 09 Nov 2012 08:56:22 +0100, Ulrich Eckhardt wrote: > Am 08.11.2012 21:29, schrieb Terry Reedy: >> On Thu, Nov 8, 2012 at 8:55 AM, Ulrich Eckhardt >> <ulrich.eckhardt@dominolaser.com> wrote: >>>> On 3.3, it gives me a "TypeError: object.__init__() takes no >>>> parameters". To some extent, this makes sense to me, because the int >>>> subobject is not initialized in __init__ but in __new__. As a >>>> workaround, I can simple drop the parameter from the call. >> >> Just drop the do-nothing call. > > Wait: Which call exactly? > > Do you suggest that I shouldn't override __init__? The problem is that I > need to attach additional info to the int and that I just pass this to > the class on contstruction. No, of course not. If you need to override __init__, you need to override __init__. > Or, do you suggest I don't call super().__init__()? That would seem > unclean to me. On the contrary: calling super().__init__ when the superclass does something you don't want (i.e. raises an exception) is unclean. Since the superclass __init__ does nothing, you don't need to call it. Only inherit behaviour that you actually *want*. In Python 3.3: py> class X(int): ... def __init__(self, *args): ... super().__init__(*args) # does nothing, call it anyway ... py> x = X(22) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __init__ TypeError: object.__init__() takes no parameters It is apparently an oversight, or a bug, that it ever worked in older versions. > Note that even though I derive from an immutable class, the resulting > class is not formally immutable. Maybe exactly that is the thing that > the developers did not want me to do? Nope, that's irrelevant. Attaching attributes to an otherwise immutable object is fine. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-11-09 13:52 +0100 |
| Message-ID | <6tavm9-usn.ln1@satorlaser.homedns.org> |
| In reply to | #33016 |
Am 09.11.2012 12:37, schrieb Steven D'Aprano: > On Fri, 09 Nov 2012 08:56:22 +0100, Ulrich Eckhardt wrote: >> Or, do you suggest I don't call super().__init__()? That would seem >> unclean to me. > > On the contrary: calling super().__init__ when the superclass does > something you don't want (i.e. raises an exception) is unclean. > > Since the superclass __init__ does nothing, you don't need to call it. > Only inherit behaviour that you actually *want*. That one's hard to swallow for me, but maybe this is because I don't understand the Python object model sufficiently. The problem I have here is that not forwarding the __init__() to the baseclass could mean that necessary initializations are not performed, although in this very specify case I see that there aren't any. It still seems a bit like relying on an implementation details. Anyhow, I'll have to do some more reading on the the construction of objects in Python, maybe then it'll all make sense. Until then, thanks everybody for nudging me in the right direction! Uli
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2012-11-09 09:30 -0700 |
| Message-ID | <mailman.3499.1352478654.27098.python-list@python.org> |
| In reply to | #33016 |
On Fri, Nov 9, 2012 at 4:37 AM, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > In Python 3.3: > > py> class X(int): > ... def __init__(self, *args): > ... super().__init__(*args) # does nothing, call it anyway > ... > py> x = X(22) > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "<stdin>", line 3, in __init__ > TypeError: object.__init__() takes no parameters > > > It is apparently an oversight, or a bug, that it ever worked in older > versions. After reading through the bug history, I think that this change to int is incorrect, or at least incomplete. The goal of the change to object.__init__ is to enable checking for unused arguments when doing cooperative multiple inheritance, with the idea that each class in the hierarchy will remove the arguments it uses and pass the rest along. By the time object.__init__ is reached, any arguments remaining are unused and extraneous. In the case of int, int.__new__ takes up to two arguments. Due to the nature of the type system, these same two arguments are also passed to int.__init__. If each subclass removes its own arguments per the convention, then by the time int.__init__ is reached, there are still up to two *expected* arguments remaining. It should not be the responsibility of the subclasses (which one? all of them?) to remove these arguments before calling super().__init__(). The int class should have the responsibility of accepting and removing these two arguments *and then* checking that there is nothing left over. In Python 3.2, int.__init__ happily accepted the int arguments, but also incorrectly accepted anything else you might pass to it, which was suboptimal for cooperative multiple inheritance. In Python 3.3, it no longer accepts unused arguments, but it also rejects arguments intended for its own class that it should accept, which as I see it makes int.__init__ *unusable* for cooperative multiple inheritance. I realize that the recommendation in the bug comments is to use __new__ instead of __init__ for subclasses of immutable types. But then why have them call __init__ in the first place? Why even fuss over what arguments int.__init__ does or does not accept if we're not supposed to be calling it at all? And why is that deprecation not mentioned anywhere in the documentation, that I can find?
[toc] | [prev] | [next] | [standalone]
| From | Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> |
|---|---|
| Date | 2012-11-12 14:48 +0100 |
| Message-ID | <9bb7n9-kie.ln1@satorlaser.homedns.org> |
| In reply to | #33016 |
Am 09.11.2012 12:37, schrieb Steven D'Aprano: > In Python 3.3: > > py> class X(int): > ... def __init__(self, *args): > ... super().__init__(*args) # does nothing, call it anyway > ... > py> x = X(22) > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "<stdin>", line 3, in __init__ > TypeError: object.__init__() takes no parameters > > > It is apparently an oversight, or a bug, that it ever worked in older > versions. I'm not really convinced that the overall behaviour is sound: py> x = 42 py> x.__init__() py> x.__init__(1) py> x.__init__(1,2) py> x.__init__(1,2,3) py> x.__init__(1,2,3,4) Neither of these seem to care about the number and type of parameters. On the other hand: py> y = object() py> y.__init__() py> y.__init__(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object.__init__() takes no parameters So, for some reason that I don't understand yet, my call to the superclass' init function skips a class, but only when called with super(). Confused greetings! Uli
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web