Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #32958 > unrolled thread

int.__init__ incompatible in Python 3.3

Started byUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
First post2012-11-08 16:55 +0100
Last post2012-11-12 14:48 +0100
Articles 8 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  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

#32958 — int.__init__ incompatible in Python 3.3

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-11-08 16:55 +0100
Subjectint.__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]


#32963

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-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]


#32978

FromTerry Reedy <tjreedy@udel.edu>
Date2012-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]


#33003

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-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]


#33016

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-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]


#33021

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-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]


#33032

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-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]


#33182

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2012-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