Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python subclassing a class with custom __new__

There's a class (not created by me, from a 3rd party library) that has no __init__ declared (other than object's __init__), and this is its __new__:

def __new__(cls, uid):
    self = super().__new__(cls, uid)
    self._info = get_info_from_uid(uid)
    return self

I can't see why didn't they just use __init__ here, but they didn't.

Now I'd like to subclass this and give it an additional attribute; before I checked the source-code of the class (only the documentation), I thought it's just using __init__ like others, so here's what I did:

class MyClass(TheirClass):
    def __init__(self, uid, my_stuff=()):
        super().__init__(uid)
        self.my_stuff = my_stuff

Apparently this raised a TypeError from object.__init__() not taking parameteres. How should I subclass such class with __new__? Do I just override the __new__ method to?

like image 241
Markus Meskanen Avatar asked Jan 30 '15 13:01

Markus Meskanen


1 Answers

Since the original project stuck with __new__ and using both __init__ and __new__ together in sub-classes can get very tricky indeed, you'll have to use __new__ in subclasses as well:

class MyClass(TheirClass):
    def __new__(cls, uid, my_stuff=()):
        self = super().__new__(cls, uid)
        self.my_stuff = my_stuff
        return self

Perhaps the original author felt that the type should be treated as an immutable.

You can still use __init__ if you insist, but you'll have to specify a __new__ method anyway to account for the extra argument your __init__ now takes:

class MyClass(TheirClass):
    def __new__(cls, uid, *args):
        # ignore the extra arguments
        return super().__new__(cls, uid)

    def __init__(self, uid, my_stuff=()):
        self.my_stuff = my_stuff
like image 80
Martijn Pieters Avatar answered Nov 14 '22 21:11

Martijn Pieters