4

I have a class Parent. I want to define a __new__ for Parent so it does some magic upon instantiation (for why, see footnote). I also want children classes to inherit from this and other classes to get Parent's features. The Parent's __new__ would return an instance of a subclass of the child class's bases and the Parent class.

This is how the child class would be defined:

class Child(Parent, list):
    pass

But now I don't know what __new__ to call in Parent's __new__. If I call object.__new__, the above Child example complains that list.__new__ should be called. But how would Parent know that? I made it work so it loops through all the __bases__, and call each __new__ inside a try: block:

class Parent(object):
    def __new__(cls, *args, **kwargs):
        # There is a special wrapper function for instantiating instances of children
        # classes that passes in a 'bases' argument, which is the __bases__ of the
        # Children class.
        bases = kwargs.get('bases')
        if bases:
            cls = type('name', bases + (cls,), kwargs.get('attr', {}))
            for base in cls.__mro__:
                if base not in (cls, MyMainType):
                    try:
                        obj = base.__new__(cls)
                        break
                    except TypeError:
                        pass
            return obj
        return object.__new__(cls)

But this just looks like a hack. Surely, there must be a better way of doing this?

Thanks.

  • The reason I want to use __new__ is so I can return an object of a subclass that has some dynamic attributes (the magic __int__ attributes, etc) assigned to the class. I could have done this in __init__, but I would not be able to modify self.__class__ in __init__ if the new class has a different internal structure, which is the case here due to multiple inheritance.
Community
  • 1
  • 1
Oliver Zheng
  • 7,831
  • 8
  • 53
  • 59
  • I had an answer, but then I realized I totally misunderstood your question, so I deleted it and then revised it and undeleted it. I think the new answer addresses your problem and will work. I have done some minimal testing. – Omnifarious Jan 26 '10 at 03:03
  • You are distinctly into the realm of metaclass programming, both tricky and wondrous. The reason you are getting the error with __new__ is that `object` and builtin types like `list` and `dict`, as well as objects with `__slots__` all have different memory layouts. More insight in what you are trying to accomplish would help a great deal. You might also want to look at using a class decorator or a function in the __metaclass__ slot. – Shane Holloway Jan 26 '10 at 03:27
  • I'm not sure why you would need to modify self.__class__, you don't mention that. And if you need to do complicated magic like this in __new__, one solution that is often much easier is to not do it at all, but instead use a factory. – Lennart Regebro Jan 26 '10 at 05:59

1 Answers1

4

I think this will get you what you want:

return super(Parent, cls).__new__(cls, *args, **kwargs)

and you won't need the bases keyword argument. Unless I'm getting you wrong and you're putting that in there on purpose.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • I think this did it. I tried this before, unfortuantely, I had some other bug in my code that was preventing this from working. The super function is one heck of a magic bullet. – Oliver Zheng Jan 27 '10 at 05:48
  • 1
    @MTsoul, Indeed it is. To be honest, I don't know why that worked exactly, I just figured it would. :-) – Omnifarious Jan 27 '10 at 07:39