Reputation: 1376
I have two classes with method foo
:
Foo = type('Foo', (object,), {'foo': lambda s: 'Foo method'})
Bar = type('Bar', (object,), {'foo': lambda s: 'Bar method'})
And I have some other class which I need to be subclassed of one of the above classes according to the parameter.
My Solution:
class Subject(object):
def __new__(cls, key):
base = (Foo if key else Bar)
name = cls.__name__ + base.__name__
dict_ = dict(cls.__dict__.items() + base.__dict__.items())
bases = (base, cls)
t = type(name, bases, dict_)
return base.__new__(t)
def bar(self):
return 'Subject method'
Testing:
print(Subject(True).foo(), Subject(True).bar())
print(Subject(False).foo(), Subject(False).bar())
Output:
('Foo method', 'Subject method')
('Bar method', 'Subject method')
Is this solution safe enough? Or I need something more to know? Is there more pythonic way to do this kind of unregular stuff?
Upvotes: 1
Views: 115
Reputation: 5613
If you avoid metaclasses (magic) the code will be more readable (and thus more pythonic). Go with the approaches suggested by Chris and Raymond, i.e.
use composition:
class Subject(object):
def __init__(self, key):
self.foo = (Foo if key else Bar)().foo
def bar(self):
return 'Subject method'
or use a factory function:
def Subject(key):
class Subject(Foo if key else Bar):
def bar(self):
return 'Subject method'
return Subject()
Upvotes: 1
Reputation: 208475
Here is an example of some unexpected behavior that could be caused by your current method, even if Subject
overrides something from Foo
or Bar
, the Foo
or Bar
version of the method will be called:
class Subject(object):
# all of your current methods as they were above
def foo(self):
return 'Subject foo'
>>> Subject(False).foo()
'Bar method'
Even though you could fix this by putting base.__dict__.items()
before cls.__dict__.items()
on the line where you create dict_
, I would suggest moving away from this approach entirely, possibly using Chris Lutz' comment or Raymond's answer.
If all you are interested in doing is having a dynamic method, I would suggest the following. Create private versions of the methods inside of the Subject
class, and then assign whichever method you want to use in Subject.__init__()
:
class Subject(object):
def __init__(self, key):
self.foo = self._Foo_foo if key else self._Bar_foo
def _Foo_foo(self):
return 'Foo method'
def _Bar_foo(self):
return 'Bar method'
>>> Subject(True).foo()
'Foo method'
>>> Subject(False).foo()
'Bar method'
Upvotes: 0
Reputation: 226336
I think most people would see the above code and recommend using composition rather than inheritance. Subject would define a foo method that dispatched to the correct class based on the boolean value.
Alternatively, you could use a factory function to create either a Foo or Bar as needed.
def subject(selector):
'Factory function that chooses between Foo and Bar'
return Foo() if selector else Bar()
If needed, make both Foo and Bar inherit from a common class so that the factory function always returns an instance of a subclass of the common class.
Upvotes: 2