Python-list Info Page

Bengt Richter bokr at oz.net
Sun Oct 31 01:48:17 EDT 2004


On Sat, 30 Oct 2004 23:43:40 -0400, Gary Robinson <grobinson at transpose.com> wrote:

>I am trying to understand a couple of nuances of inheriting from the 
>tuple type.
>
>The following class definition works nicely:
>
It may appear to, but it's not doing what I think you think it is ;-)

>class Test(tuple):
>    def __init__(self, tup):
>        self.x = 5
>        self = tup
The above is just rebinding the local name 'self', which _was_ bound to
your instance (which gave you the access to make self.x = 5 work), but
as soon as __init__ exits, that last self binding is gone.

>a = Test((1,2))
>print a, a.x
>
>The result of that print is "(1, 2), 5". 
Yes, the (1,2) that you passed to Test, got passed on to tuple.__new__, because
there was no Test.__new__, and so the normal tuple creation took place. Then __init__
got called.

>
>But the following version:
>
>class Test(tuple):
>    def __init__(self, tup):
>        self = tup
>        self.x = 5
>a = Test((1,2))
>
That's because you changed the self binding to whatever was passed in as tup
(a real python tuple (1,2), not an instance of your subclass)
and you effectively tried to hang an attibute x onto a native tuple, like:

 >>> self = (1, 2)
 >>> self.x = 5
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 AttributeError: 'tuple' object has no attribute 'x'


>blows up in Python 2.3, complaining that attribute x doesn't exist.
>
>I'm not clear on why one works and not the other, or whether there's a 
Neither one worked ;-)

>better syntax for assigning an attribute to a tuple subclass in the 
>__init__() method.
>
>Anyone have any  thoughts??
If you want to modify tuple creation from the constructor argument, you will
have to define a __new__, e.g.,

 >>> class Test(tuple):
 ...     def __new__(cls, tup):
 ...         inst = tuple.__new__(cls, tup+('mod from __new__',))
 ...         inst.x = 5
 ...         return inst
 ...
 >>> a = Test((1,2))
 >>> a
 (1, 2, 'mod from __new__')
 >>> type(a)
 <class '__main__.Test'>
 >>> a.x
 5

You notice I did the __init__ job of self.x = 5 right in __new__, but we could
have left it for init:

 >>> class Test(tuple):
 ...     def __new__(cls, tup):
 ...         inst = tuple.__new__(cls, tup+('mod from __new__',))
 ...         return inst
 ...     def __init__(self, *ignore):
 ...         self.x = 5
 ...
 >>> a = Test((1,2))
 >>> a.x
 5
 >>> a
 (1, 2, 'mod from __new__')

__init__ gets called with the same arg as __new__, so you have to allow for it.
The *args format is handy, since you can ignore zero or more args without knowing
how many are being passed. Otherwise ignore=None would accept and ignore zero or one args.

You could call init yourself from __new__ by doing inst.__init__(), but that doesn't
eliminate the automatic call after you leave __new__ returning inst, so you'd get two calls,
one with no arg and one with the tup arg. (Note that just specifying tup as an arg means
exactly one arg must be supplied. E.g., tup=() would give you a default empty tuple.
Also, if you wanted more args for __init__, you'd have to allow for them in both arg lists,
e.g.,

 >>> class Test(tuple):
 ...     def __new__(cls, tup, *ignore, **ignorekw):
 ...         inst = tuple.__new__(cls, tup+('mod from __new__',))
 ...         return inst
 ...     def __init__(self, ignoretup, ones=111, two='two', **kw):
 ...         self.x = 5
 ...         self.ones = ones
 ...         self.two = two
 ...         self.kwargs = kw
 ...
 >>> a = Test((3,4),'string ones', 2, keyword='arg', another=123)
 >>> a
 (3, 4, 'mod from __new__')
 >>> a.x
 5
 >>> a.ones
 'string ones'
 >>> a.two
 2
 >>> a.kwargs
 {'another': 123, 'keyword': 'arg'}

Of course, you could have used the extra parameters to do something
in place of the ('mod from __new__',) nonsense ;-)

Hm, ... I'm not sure I like not having control of the call to __init__ even 
when I've defined __new__. What is the right way to do that?

Regards,
Bengt Richter



More information about the Python-list mailing list