I want to dynamically add a mixin class in an object without effecting any other objects of that class. I have an external service that determine which mixin should be used for a particular object.
I am currently using a metaclass to dynamically add a base class to an object. But the issue is, if I change base class of any object, it effects all other objects as well. Here is a simplified sample code snippet:
class M(type):
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
if hasattr(obj, "mixin"):
obj.__class__.__bases__ = (obj.mixin,) + obj.__class__.__bases__
return obj
class MixinA:
name = "A"
attr1 = 100
class MixinB:
name = "B"
attr2 = 200
class A(metaclass=M):
pass
class B(A):
def __init__(self, some_value):
self.mixin = external_function(some_value)
b1 = B('X') # Let's say it should use MixinA
print(b1.name, b1.__class__.__base__)
b2 = B('Y') # Let's say it should use MixinB
print(b2.name, b2.__class__.__base__)
print(b1.name, b1.__class__.__base__)
This will output:
A <class '__main__.MixinA'>
B <class '__main__.MixinB'>
B <class '__main__.MixinB'>
What can I do so that changing base class of b2
will not effect b1
. So the output should be:
A <class '__main__.MixinA'>
B <class '__main__.MixinB'>
A <class '__main__.MixinA'>
Or is there any other ways where I can access/execute all the properties/methods of the appropriate mixin applied dynamically to that object.
Edit: Updated description and code snippet for more clarity.
Getting the inspiration from @chepner comments and the solution proposed, I have 2 solutions. The idea is same as creating a new dynamic class that compose of both the original class and the mixin class.
Solution 1: Using Metaclass
class M(type):
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
if hasattr(obj, "mixin"):
new_cls = type(cls.__name__ + obj.mixin.__name__, (cls, obj.mixin), {})
new_obj = type.__call__(new_cls, *args, **kwargs)
return new_obj
return obj
Solution 2: Using Factory
def factory(cls, *args, **kwargs):
obj = cls(*args, **kwargs)
if hasattr(obj, "mixin"):
new_cls = type(cls.__name__ + obj.mixin.__name__, (cls, obj.mixin), {})
new_obj = new_cls(*args, **kwargs)
return new_obj
return obj