explicit call to __init__(self) in subclass needed?

Bruno Desthuilliers bruno.42.desthuilliers at websiteburo.invalid
Mon Sep 21 04:06:25 EDT 2009


Andrew MacKeith a écrit :
> 
> 
> Bruno Desthuilliers wrote:
>> Andrew MacKeith a écrit :
>>> I create a class like this in Python-2.6
>>>
>>>  >>> class Y(str):
>>> ...   def __init__(self, s):
>>> ...      pass
>>> ...
>>>  >>> y = Y('giraffe')
>>>  >>> y
>>> 'giraffe'
>>>  >>>
>>>
>>> How does the base class (str) get initialized with the value passed 
>>> to Y.__init__() ?
>>
>> It happens in the __new__ method (which is the proper "constructor")
>>
>> class Y(str):
>>     def __new__(cls, value, foo="foo"):
>>         instance = str.__new__(cls, value)
>>         instance.foo = foo
>>         return instance
>>
>>     def __repr__(self):
>>         return "<%s(%s, %s)>" % (type(self).__name__, self, self.foo)
>>
>>
>>> Is this behavior specific to the str type,  or do base classes not need
>>> to be explicitly initialized?
>>
>> When you override a method in a derived class, it's your 
>> responsability to call on the parent(s) class(es) implementation. 
>> __init__ is not an exception to that rule. The point is that since 
>> __init__ works by mutating the newly created instance, it makes no 
>> sense to have a distinct __init__ for immutable types - which have 
>> their "value" set once for all at creation time.
> 
> Thanks for the explanation, Bruno.
> 
> I have been successfully using a class derived from str, and in that
> class I add a lot of extra data to the derived class instance in the
> __init__ method of the derived class, so it is possible to mutate the
> derived class __dict__, even if the base class data remains immutable.

Indeed. What I said was that Python builtins immutable types didn't use 
the initializer, not that you couldn't use an initializer in derived 
classes. I gave an example using __new__ because, quite often when 
subclassing immutable types, peoples want to access the initial value 
_before_ the call to the base class.

> 
> See example below.
> 
> The __init__ must have the same arguments as the base class.

The initializer (__init__) must have the same arguments as the 
constructor (__new__). This is true for all and every classes.

(snip)

> 
> But you can get bitten if you do something that returns a new object
> 
>  >>> y += 'Tail'
>  >>> y
> 'ParrotTail'
>  >>> y.color
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: 'str' object has no attribute 'color'
>  >>>
>  >>> y.__class__
> <type 'str'>
>  >>>

You need to override a couple other __magic_methods__ to make this work. 
You'll find relevant documentation here:

http://docs.python.org/reference/datamodel.html#special-method-names

In the above case, you want to override at least the __add__ method:

class Y(str):
     def __new__(cls, value, foo="foo"):
         instance = str.__new__(cls, value)
         instance.foo = foo
         return instance

     def __repr__(self):
         return "<%s(%s, %s)>" % (type(self).__name__, self, self.foo)

     def __add__(self, other):
         return type(self)(str(self) + other, self.foo)

 >>> y = Y("foo", foo="bar")
 >>> y
<Y(foo, bar)>
 >>> y += "baaz"
 >>> y
<Y(foobaaz, bar)>
 >>>

HTH



More information about the Python-list mailing list