Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Is it possible to construct an instance of a child class from an instance of a parent class?

This may be a terrible idea (feel free to tell me if it is), but I'm exploring the boundaries of Python, and I could see how this could be useful (in fact, I'm looking at a potential case for it right now).

Here is the setup:

---(API File)---

class APINode(object):
    def __init__(self, *args, **kwargs):
        # initialize some instance variables

    def render(self, arg1, arg2):
        # do some stuff and return something

def api_function( arg1, arg2 ):
    # do some stuff
    return APINode(args, kwargs)

---- (My file) ----

class MyNode(APINode):
    def render(self, arg1, arg2):
        #My override of APINode's render 


def my_function( arg1, arg2 ):
    api_parent_instance = api_function( arg1, arg2 )
    #Can I some how create an instance of MyNode from api_parent_instance here?

I want to modify the output of the api_function slightly, basically just to override the render function in the object it returns. I feel like my options are: (1, yuck) to copy the contents of api_function into my_function, but just construct and return a MyNode instead of an APINode, or (2, maybe?) to just call api_function from my_function, let its do its work--constructing and returning an object of type APINode, and then I can somehow create a MyNode from that object in order to override that one method.

It boils down to: In Python, is it possible to construct an instance of a child class from an instance of a parent class?

(Look familiar or wondering what the actual case is? I'm trying to extend a Django template tag.)

like image 257
B Robster Avatar asked Jul 09 '11 02:07

B Robster


5 Answers

I think you'll be happier with MyNode wrapping APINode, rather than extending it. You can implement your own render() method, and delegate all others to the wrapped APINode. Then you'll be able to create a new MyNode from an existing APINode.

There's no way to make a child instance from a parent instance. The parent instance is an instance of APINode, you can't change an object's class.

like image 64
Ned Batchelder Avatar answered Nov 02 '22 20:11

Ned Batchelder


Don't do this at home.

>>> class A:
...     def hi(self):
...         print "I am an A!"
... 
>>> class B:
...     def hi(self):
...         print "I am a B!"
... 
>>> a = A()
>>> a.hi()
I am an A!
>>> # Doing this will likely lead to hard to find bugs.
>>> a.__class__ = B
>>> a.hi()
I am a B!
>>> 

Monkey patch the API instead!

def render(self, arg1, arg2):
    #My override of APINode's render 
APINode.render = render
#congratulations, now APINode uses your render function.

This will still likely lead to hard-to-find bugs, but it's a bit cleaner.

like image 23
ironchefpython Avatar answered Nov 02 '22 22:11

ironchefpython


Override the allocator.

class B(object):
  def __new__(cls, val):
    if val:
      return D()
    return super(B, cls).__new__(cls)

  def foo(self):
    return 'bar'

class D(object):
  def foo(self):
    return 'baz'

b1 = B(False)
b2 = B(True)
print b1.foo()
print b2.foo()
like image 20
Ignacio Vazquez-Abrams Avatar answered Nov 02 '22 22:11

Ignacio Vazquez-Abrams


This is not something I would recommend, but anyways:

>>> class Monkey(object):
...     def eat(self, smth):
...             if self.likes(smth):
...                     print "Yummy!"
...             else:
...                     print "Yuck!"
...     def likes(self, smth):
...             return smth == "banana"
... 
>>> m = Monkey()
>>> m.eat("banana")
Yummy!
>>> m.eat("cheezburger")
Yuck!
>>> def patch(self, smth):
...     return True
...
>>> m.likes = type(m.likes)(patch, m.likes, Monkey)
>>> m.eat("cheezburger")
Yummy!
>>> 

In your particular case it would look like:

def my_render(self, arg1, arg2):
    #My override of APINode's render 

def my_function( arg1, arg2 ):
    api_parent_instance = api_function( arg1, arg2 )
    api_parent_instance.render = type(api_parent_instance.render)(
        api_parent_instance,
        api_parent_instance.render,
        api_parent_instance.__class__)

    ...
like image 1
Mike Ivanov Avatar answered Nov 02 '22 20:11

Mike Ivanov


Here is a very simple and safe way of generating a new instance of a child class from a method of the parent class:

class Parent(object):
    def __init__(self):
        pass

    def generate_new_child_object(self):
        print "Generating a new object of type " + str(type(self))
        return type(self)()

class Child(Parent):
    pass

And you can check this in the terminal as follows:

>>> child = Child()
>>> type(child)
<class 'Child'>
>>> generated_child = child.generate_new_child_object()
Generating a new object of type <class 'Child'>
>>> type(generated_child)
<class 'Child'>
like image 1
galarant Avatar answered Nov 02 '22 20:11

galarant