1

I have class Base. I'd like to extend its functionality in a class Derived. I was planning to write:

class Derived(Base):
  def __init__(self, base_arg1, base_arg2, derived_arg1, derived_arg2):
    super().__init__(base_arg1, base_arg2)
    # ...
  def derived_method1(self):
    # ...

Sometimes I already have a Base instance, and I want to create a Derived instance based on it, i.e., a Derived instance that shares the Base object (doesn't re-create it from scratch). I thought I could write a static method to do that:

b = Base(arg1, arg2) # very large object, expensive to create or copy
d = Derived.from_base(b, derived_arg1, derived_arg2) # reuses existing b object

but it seems impossible. Either I'm missing a way to make this work, or (more likely) I'm missing a very big reason why it can't be allowed to work. Can someone explain which one it is?

[Of course, if I used composition rather than inheritance, this would all be easy to do. But I was hoping to avoid the delegation of all the Base methods to Derived through __getattr__.]

8
  • "a Derived instance that shares the Base object (doesn't re-create it from scratch)." - That makes no sense. There is no Base instance when you instantiate the Derived class. There is only a Derived object, so there is nothing to share or re-create. I think you shouldn't subclass Base at all. Commented Dec 10, 2012 at 12:12
  • Also, since the Base and Derived has different parameters to __init__ you should in general avoid super(). Commented Dec 10, 2012 at 12:13
  • @LennartRegebro: regarding your 1st comment, the Base instance I'm trying to share is b. It was created before I was trying to create a Derived instance. Commented Dec 10, 2012 at 20:45
  • @LennartRegebro: regarding your 2nd comment, if I have inheritance, isn't it very common that a derived class extends the functionality of the base class in such a way that it needs extra parameters to __init__? If I don't use super(), there's no way to do inheritance at all, is there? (Well, unless the base __init__ just happens to work fine in the derived class, without any modification.) Commented Dec 10, 2012 at 20:47
  • Since you are trying to share an instance with one or several other instances, subclassing the class of the first instance is not a solution. It does in fact not help you at all. Subclassing is about sharing code between classes, not data between instances. Commented Dec 11, 2012 at 0:05

3 Answers 3

2

Rely on what your Base class is doing with with base_arg1, base_arg2.

class Base(object):
    def __init__(self, base_arg1, base_arg2):
        self.base_arg1 = base_arg1
        self.base_arg2 = base_arg2
        ...

class Derived(Base):
    def __init__(self, base_arg1, base_arg2, derived_arg1, derived_arg2):
        super().__init__(base_arg1, base_arg2)
        ...

    @classmethod
    def from_base(cls, b, da1, da2):
        return cls(b.base_arg1, b.base_arg2, da1, da2)
Sign up to request clarification or add additional context in comments.

5 Comments

That would work but only if __init__ was a cheap method that simply assigns its arguments to the instance. Unfortunately, it's far from that. This is the main reason Base instances are expensive to create.
@max I understood that Base is expensive, but what's problem with adding 2 lines of code to __init__ in order to store 2 additional attributes?
base_arg1 is a filename, base_arg2 is the format description, Base.__init__ parses the (usually huge) file and stores the result in an internal data structure.
@max in this case you don't need super. possible, you even don't need inheritance (you "has a" instead of "is a").
Not only do you not need super, you shouldn't use it as the two methods have different parameters. I'd also rather go for peprs solution and store b as an attribute.
1

The alternative approach to Alexey's answer (my +1) is to pass the base object in the base_arg1 argument and to check, whether it was misused for passing the base object (if it is the instance of the base class). The other agrument can be made technically optional (say None) and checked explicitly when decided inside the code.

The difference is that only the argument type decides what of the two possible ways of creation is to be used. This is neccessary if the creation of the object cannot be explicitly captured in the source code (e.g. some structure contains a mix of argument tuples, some of them with the initial values, some of them with the references to the existing objects. Then you would probably need pass the arguments as the keyword arguments:

d = Derived(b, derived_arg1=derived_arg1, derived_arg2=derived_arg2)

Updated: For the sharing the internal structures with the initial class, it is possible using both approaches. However, you must be aware of the fact, that if one of the objects tries to modify the shared data, the usual funny things can happen.

Comments

1

To be clear here, I'll make an answer with code. pepr talks about this solution, but code is always clearer than English. In this case Base should not be subclassed, but it should be a member of Derived:

class Base(object):
    def __init__(self, base_arg1, base_arg2):
        self.base_arg1 = base_arg1
        self.base_arg2 = base_arg2

class Derived(object):
    def __init__(self, base, derived_arg1, derived_arg2):
        self.base = base
        self.derived_arg1 = derived_arg1
        self.derived_arg2 = derived_arg2

    def derived_method1(self):
        return self.base.base_arg1 * self.derived_arg1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.