Welcome, guest | Sign In | My Account | Store | Cart
1

Whenever a superclass implements a __init__ method to initialize its attributes, subclasses derived from it have to invoke the __init__ method of the superclass. This recipe is a different mechanism of initializing the attributes of a superclass with default values, achieved by overriding the __new__ method.

Python, 44 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Simple inheritance case
class super1 (object):
    def __new__(typ, *args, **kwargs):
        obj = object.__new__(typ, *args, **kwargs)
        obj.attr1 = []
        return obj

class derived1(super1):
    def __init__(self, arg4, **kwargs):
        self.attr4 = arg4
        self.attr5 = kwargs['arg5']

if '__main__'==__name__:
    d1 = derived1(222, arg5=333)
    d1.attr1.append(111)
    print d1.attr1, d1.attr4, d1.attr5,
    print isinstance(d1, super1)

# Multiple inheritance case
class super2 (object):
    def __new__(typ, *args, **kwargs):
        obj = object.__new__(typ, *args, **kwargs)
        obj.attr2 = 222
        return obj

import copy
class derived2 (super1, super2):
    def __new__(typ, *args, **kwargs):
        objList = [sup.__new__(typ, *args, **kwargs)
                   for sup in derived2.__bases__]
        for obj in objList[1:]:
            objList[0].__dict__.update(copy.deepcopy(obj.__dict__))
        objList[0].attr3 = 333
        return objList[0]
    def __init__(self, arg4, **kwargs):
        self.attr1.append(111)
        self.attr4 = arg4
        self.attr5 = kwargs['arg5']

if '__main__'==__name__:
    d1 = derived2(444, arg5=555)
    print d1.attr1, d1.attr2, d1.attr3, d1.attr4, d1.attr5,
    print isinstance(d1, super1),
    print isinstance(d1, super2)

The code shows how to override the __new__ method in order to initialize instance attributes with default values. This may be desirable to ensure that attributes are initialized correctly in subclasses regardless of how the __init__ methods of those subclasses are implemented (e.g., it ensures that the superclass’s attributes are initialized before the subclass’s attributes). Overriding the __new__ method can be useful also in other cases. This recipe can be seen as a more general mechanism to follow whenever overriding the __new__ method.

Class ‘super1’ overrides its __new__ method in order to initialize its attributes with default values. This allows its subclass ‘derived1’ to define its own method __init__ without invoking a parent class’s __init__ method.

Class ‘super2’ is implemented in a similar way, but class ‘derived2’ shows how to use such a mechanism in the case of multiple inheritance. Attributes from the superclasses are inherited in this example from left to right, which means that attributes in a superclass can be overridden by attributes in a superclass that succeeds the first one in the inheritance sequence of class ‘derived2’. Other implementations can use a different inheritance order by changing the order in the list ‘objList’.

One object is created for each superclass, but one of them is chosen to update its own dictionary __dict__ with the entries from all the other objects. Finally, this object is the new object that is returned by the __new__ method.

5 comments

Shalabh Chaturvedi 12 years, 6 months ago  # | flag

Using cooperative super call useful. Using super to call next method might be useful in this case. It reduces complexity of the derived __new__ when multiple inheritance is involved. Here is an updated version.

# Simple inheritance case
class super1 (object):
    def __new__(typ, *args, **kwargs):
        obj = super(super1, typ).__new__(typ, *args, **kwargs)
        obj.attr1 = []
        return obj

# Multiple inheritance case
class super2 (object):
    def __new__(typ, *args, **kwargs):
        obj = super(super2, typ).__new__(typ, *args, **kwargs)
        obj.attr2 = 222
        return obj

class derived2 (super1, super2):
    def __new__(typ, *args, **kwargs):
        obj = super(derived2, typ).__new__(typ, *args, **kwargs)
        obj.attr3 = 333
        return obj

    def __init__(self, arg4, **kwargs):
        self.attr1.append(111)
        self.attr4 = arg4
        self.attr5 = kwargs['arg5']

if '__main__'==__name__:
    d1 = derived2(444, arg5=555)
    print d1.attr1, d1.attr2, d1.attr3, d1.attr4, d1.attr5,
    print isinstance(d1, super1),
    print isinstance(d1, super2)
Dan Perl (author) 12 years, 6 months ago  # | flag

Re: Using cooperative super call useful. I contemplated using super() at some point before submitiing the recipe, but I was not clear yet on the behavior of super and of cooperative classes. I have finally read more on the topic and now I get it. You're right, Shalabh, this is a better solution and it actually makes the recipe kind of trivial.

I am not going to make the changes yet on the recipe and I will wait for a verdict from the editors (i.e., whether they would still be interested in the recipe). If there is still an interest, here is what I suggest.

Shalabh's code would be the preferred solution and should be presented first. That solution depends however on the superclasses to be cooperative classes. I tried it, and if super1 does not invoke super(...), the implementation of derived2 breaks (derived2.attr2 is missing).

My original code can then follow with the argument that it should be used when the superclasses may not be cooperative classes. This second solution offers also more control on the order in which attributes are inherited. A note that should be added is that changing that order may be dangerous however, because the order of inheritance for methods is not changed and a dependence of the methods on the attributes may cause trouble.

Derrick Wallace 12 years, 6 months ago  # | flag

Super or no super, still useful. I find this recipe to still be useful and educational even after the addition of the super function. Until now, I have not seen much discussion on the __new__ method. And in this recipe, Dan does a great job of exposing the usefulness of the __new__ method. As a result, I am definitely encouraged to exploit the __new__ method in my Python code. And kudos also to Shalabh as well for his contribution of the super function as well! Thanks!

Mario Vilas 8 years ago  # | flag

How about using __new__ to implement singletons?

class MySingleton (object):
def __new__(typ, *args, **kwargs):
    try:
        return MySingletonInstance
    except:
        return object.__new__(typ, *args, **kwargs)

MySingletonInstance = MySingleton()

This example is admittedly crude since it doesn't handle subclassing, and there's probably a better way to check for the existence of the MySingletonInstance symbol, but you get the idea. :)

Just for fun, this second example would be a weird way to implement singletons by returning always the class object instead of new instances. It works only if all your methods are static or class methods, of course.

class MySingleton (object):
    def __new__(typ, *args, **kwargs):
        return MySingleton
kyle roux 2 months ago  # | flag

you could also implement Singleton using new and a class attribute to store the instance:

class MyNewSingleton(object):
    _instance = None
    def __new__(typ,*args,**kwargs):
        if typ._instance is None:
            typ._instance = object.__new__(typ,*args,**kwargs)
        return typ._instance

pretty simple, with no need for odd globals or try/except blocks

Add a comment

Sign in to comment