Reputation: 19375
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
.
Upvotes: 5
Views: 1614
Reputation: 881595
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.
Upvotes: 0
Reputation: 27936
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
Upvotes: 4
Reputation: 54882
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.
Upvotes: 1
Reputation: 129764
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)
Upvotes: 0
Reputation: 879421
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)
Upvotes: 7