3

I have a function which return instances of the class Parent:

def generateParent():
   do_stuff
   return Parent(some_parameters)

Now I want to init a subclass of Parent with the results of a call to generateParent():

class Child(Parent):
    def __new__():
        return generateParent(some_other_parameters) 

The problem is, when I override some methods from Parent in Child and then call them in instances of Child in my program, the original Parent method gets called instead of the new one from Child. Am I doing something wrong here? Am I using the correct design here for my task?

EDIT: I don't have access neither to Parent nor generateParent()

Solution(thanks to @Paul McGuire's answer):

class Child(object):
    def __init__(self):
        self.obj = generateParent()

    def __getattr__(self, attr):
        return getattr(self.obj, attr)
Community
  • 1
  • 1
elyase
  • 39,479
  • 12
  • 112
  • 119
  • 4
    You are, on purpose, not returning a child from the constructor, but a parent, and then you are surprised that it acts like a parent? – Gareth Latty Jan 06 '13 at 00:02
  • I get it, how can I then extend a class but also init it with instances of the parent returned by a function? – elyase Jan 06 '13 at 00:11

3 Answers3

5

Since generateParent is not your code, then instead of inheritance, you might want to use containment and delegation. That is, instead of defining a subclass, define a wrapper class that contains the generated object, forwards method calls to it when needed, but can add new behavior or modified behavior in the wrapper.

In this question, the OP had a similar situation, having a class generated in a libary, but wanting to extend the class and/or modify some behavior of the class. Look at how I added a wrapper class in that question, and you might consider doing something similar here.

Community
  • 1
  • 1
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
1

Here's one way to do it:

def generateChild(params):
    p = generateParent(params)
    p.__class__ = Child
    return p

class Child(Parent):
    # put method overrides etc here

childinstance = generateChild(some_params)
Marcin
  • 48,559
  • 18
  • 128
  • 201
Eric
  • 95,302
  • 53
  • 242
  • 374
  • Using this, how would I go creating and using Child objects? Do I always have to call generateChild() before? – elyase Jan 06 '13 at 00:18
  • You could put those two lines (`p = generateParent(params); p.__class__ = Child`) in the `__new__` method of `Child`. – David Robinson Jan 06 '13 at 00:22
  • @DavidRobinson I find your suggestion more 'pythonic' as I can create objects with new_child = Child() – elyase Jan 06 '13 at 00:30
  • I've used this technique myself, but you really need to be careful with it. Note that `Child.__init__` will *never* be called in this case, and any instance variables defined in `Child.__init__`will not be created, which could cause problems in methods in Child that depend on them. If you do this, write `Child.__init__` to call a method like `Child.addChildAttributes()` that would add any special attributes, then in `generateChild()`, after setting `p.__class__`, then call `p.addChildAttributes()`. – PaulMcG Jan 06 '13 at 00:48
  • Yep I am getting errors with this solution as __init__ (from Parent?) is getting called with my init parameters and I haven't defined(or need to) any __init__. – elyase Jan 06 '13 at 01:39
1
  1. Perhaps you want generateParent to be able to make instances of other classes:

    def generateParent(cls=Parent):
        do_stuff
        return cls(some_parameters)
    

    Now this will make a Child object:

    child = generateParent(Child)
    
  2. Or perhaps you want Parent and all of its derived classes to use common initialization code?

    class Parent(object):
        def __init__(self):
            do_stuff
            # init from some_parameters
    
    class Child(Parent):
        # blah..
    
  3. Make your Child object able to copy information from a created Parent object:

    class Child(Parent):
        def __init__(self):
            model_parent = generateParent()
            self.a = model_parent.a
            self.b = model_parent.b
            # etc.
    
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • interesting approach, unfortunately as I commented to a now deleted answer I don't have access to the generateParent function. – elyase Jan 06 '13 at 00:53
  • hmm, maybe update the question to include this important bit of information? – Ned Batchelder Jan 06 '13 at 00:55
  • sorry, I have added this info – elyase Jan 06 '13 at 01:01
  • Regarding 1. and 2. I dont have access to neither generateParent() nor Parent, and 3. I would have to copy a lot of properties from modelParent. – elyase Jan 06 '13 at 01:30
  • @elyase: can you provide details of how you want Child to differ from Parent? There are other possibilities, but it's hard to recommend something specific without details. – Ned Batchelder Jan 06 '13 at 01:40
  • actually I just want to overload(write my own) the `__getitem__` method from Parent. – elyase Jan 06 '13 at 01:41