CannedSpinach
CannedSpinach

Reputation: 115

Copying Attributes from Superclass Instance while Inheriting?

I'm trying to make a subclass that inherits from an instance of its superclass, and bases the majority of its attributes on the superclass attributes.

class Thing:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

class ThingWithD(Thing):
    def __init__(self, thing, d):
        self.a = thing.a
        self.b = thing.b
        self.c = thing.c
        self.d = d

Is there a more concise way to make the a, b, and c declarations inside ThingWithD.__init__()?

Upvotes: 0

Views: 847

Answers (2)

martineau
martineau

Reputation: 123521

The most concise—and object-oriented—way would probably be to just call the superclass's __init__() method and avoid the repetition:

class Thing:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

class ThingWithD(Thing):
    def __init__(self, thing, d):
        super().__init__(thing.a, thing.b, thing.c)  # Python 3 only
        self.d = d

thing = Thing(1, 2, 3)
thing_with_d = ThingWithD(thing, 4)
print('{}, {}'.format(thing_with_d.a, thing_with_d.d)) # -> 1, 4

To do the same thing in Python 2.x, you would need to make Thing a new-style class by explicitly specifying its base class as object and change the call to the superclass constructor as shown below.

If you make both of these modifications, the same code will work in both Python 2 and 3.

class Thing(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

class ThingWithD(Thing):
    def __init__(self, thing, d):
        super(ThingWithD, self).__init__(thing.a, thing.b, thing.c)
        self.d = d

Upvotes: 2

Mattew Whitt
Mattew Whitt

Reputation: 2424

With class Thing defined as such:

class Thing:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

I can think of 3 ways to do achieve what you are asking using classical inheritence. The first, is to take advantage of your known arguments and explicitly index args to pull out a to c, and d, like so:

class ThingWithD(Thing):
    def __init__(self, *args):
        self.d = args[-1]
        a_to_c = args[:-1]
        super().__init__(*a_to_c)


thing_with_d = ThingWithD(1,2,3,4)
thing_with_d.a # 1
thing_with_d.d # 4

The second, and best way, would be to convert your params to keyword arguments so that they can be more easily mixed and matched. This is the most scalable solution, and could pave the way for ThingWithE and ThingWithF.

class ThingWithD(Thing):
    def __init__(self, d=None, **kwargs):
        super().__init__(**kwargs)
        self.d = d


thing_with_d = ThingWithD(a=1,b=2,c=3,d=4)
thing_with_d.a # 1
thing_with_d.d # 4

The last way, which seems closest to what you already tried, is to use ThingWithD as a factory class that adds d to a class self referentially.

class ThingWithD(Thing):
    def __init__(self, thing, d):
        super().__init__(thing.a, thing.b, thing.c)
        self.d = d

thing = Thing(1,2,3)
thing_with_d = ThingWithD(thing, 4)
thing_with_d.a # 1
thing_with_d.d # 4

This is a strange approach, because here we are actually creating a copy of the original thing instance, and its unclear why we are inheriting from Thing at all. Instead, we could use a function that does the following.

def add_d_to_thing(thing, d):
    thing.d = d
    return thing

thing = Thing(1,2,3)
thing_with_d = add_d_to_thing(thing, 4)
thing_with_d.a # 1
thing_with_d.d # 4

This would return the same instance of thing, would add a d attribute, and is easier to read.

Upvotes: 1

Related Questions