tags:

views:

108

answers:

5

Is it possible, when instantiating an object, to pass-in a class which the object should derive from?

For instance:

class Red(object):
    def x(self):
        print '#F00'

class Blue(object):
    def x(self):
        print '#00F'

class Circle(object):
    def __init__(self, parent):
        # here, we set Bar's parent to `parent`
        self.x()

class Square(object):
    def __init__(self, parent):
        # here, we set Bar's parent to `parent`
        self.x()
        self.sides = 4

red_circle = Circle(parent=Red)
blue_circle = Circle(parent=Blue)
blue_square = Square(parent=Blue)

Which would have similar effects as:

class Circle(Red):
    def __init__(self):
        self.x()

without, however, affecting other instances of Circle.

+6  A: 

Perhaps what you are looking for is a class factory:

#!/usr/bin/env python
class Foo(object):
    def x(self):
        print('y')

def Bar(parent=Foo):
    class Adoptee(parent):
        def __init__(self):
            self.x()
    return Adoptee()
obj=Bar(parent=Foo)
unutbu
A: 

If you really need it, then you could use type constructor, e.g. within a factory function (or inside __new__ method, but this is probably safer approach):

class Foo(object):
    def x(self):
        print 'y'

class Bar(object):
    def __init__(self):
        self.x()

def magic(cls, parent, *args, **kwargs):
    new = type(cls.__name__, (parent,), cls.__dict__.copy())
    return new(*args, **kwargs)

obj = magic(Bar, parent = Foo)
PiotrLegnica
+2  A: 

It sounds like you are trying to use inheritance for something that it isn't meant for. If you would explain why you want to do this, maybe a more idiomatic and robust way to achieve your goals can be found.

Ants Aasma
Updated my question per your suggestion.
digitala
Can you elaborate a bit? I assume your goal isn't to make shapes that print their color. Do other classes need to see the methods provided by the dynamically given "parent class"? Does the "parent class" need to access attributes/methods of the child object? Given the current information, I'd suggest you store an instance of the color as an attribute and delegate any work through that attribute.
Ants Aasma
+4  A: 

I agree with @AntsAasma. You should probably consider using dependency injection. Atleast in the example given (which I'm sure is greatly simplified to illustrate your problem), the color of a shape is better represented by via a has-a relationship rather than with a is-a relationship.

You could implement this via passing in the desired color object to the constructor, storing a reference to it, and delegating the function call to this object. This greatly simplifies the implementation while still retaining the desired behavior. See an example here:

class Red(object):
    def x(self):
        print '#F00'

class Blue(object):
    def x(self):
        print '#00F'

class Shape(object):
    def __init__(self,color):
        self._color=color
    def x(self):
        return self._color.x()

class Circle(Shape):
    def __init__(self, color):
        Shape.__init__(self,color)
        self.x()

class Square(Shape):
    def __init__(self, color):
        Shape.__init__(self,color)
        self.x()
        self.sides = 4

red_circle = Circle(color=Red())
blue_circle = Circle(color=Blue())
blue_square = Square(color=Blue())

Edit: Fixed names of constructor arguments in sample code

Mark Roddy
+1: mixins a/k/a "Dependency Injection"
S.Lott
+1. In the def __init__ for Square, perhaps `parent` should be changed to `color`.
unutbu
@~unutbu: fixed the init argument names, good catch.
Mark Roddy
Yes; I wanted to inherit, and potentially override, method from a number of parent objects, however DI is probably the best bet.
digitala
A: 

As everybody else says, that's a pretty weird usage, but, if you really want it, it's surely feasible (except for the mysterious Bar that you pull out of thin air in comments;-). For example:

class Circle(object):
    def __init__(self, parent):
        self.__class__ = type('Circle', (self.__class__, parent), {})
        self.x()

This gives each instance of Circle its own personal class (all named Circle, but all different) -- this part is actually the key reason this idiom is sometimes very useful (when you want a "per-instance customized special method" with new-style classes: since the special method always gets looked up on the class, to customize it per-instance you need each instance to have a distinct class!-). If you'd rather do as much class-sharing as feasible you may want a little memoizing factory function to help:

_memo = {}
def classFor(*bases):
  if bases in _memo: return _memo[bases]
  name = '_'.join(c.__name__ for c in bases)
  c = _memo[bases] = type(name, bases, {})
  return c

(here I'm also using a different approach to the resulting class's name, using class names such as Circle_Red and Circle_Blue for your examples rather than just Circle). Then:

class Circle(object):
    def __init__(self, parent):
        self.__class__ = classFor(Circle, parent)
        self.x()

So the technique is smooth and robust, but I still don't see it as a good match to the use case you exemplify with. However, it might be useful in other use cases, so I'm showing it.

Alex Martelli
Alex, I'm getting `TypeError: __class__ must be set to new-style class, not 'NoneType' object` when trying out your second definition of `Circle` -- the one using `classFor`. I'm not sure what's wrong. Would you please take a look?
unutbu
Oops, the `classFor` function just needs to `return c`
unutbu
Right -- sorry for the typo, and good spotting @unutbu, tx.
Alex Martelli