How to dynamically manipulate MRO?

For example, i have that class structure:

class StreamMixin:
    
    def publish(self, message):
        # Stream logic here
        pass
    
    def get_message(self, message):
        # stream logic here
        pass

class SocketMixin:
    
    def publish(self, message):
        # Socket logic here
        pass
    
    def get_message(self, message):
        # Socket logic here
        pass
    
class MyPubSub(StreamMixin, SocketMixin):
    ...

And by default according to the MRO logic, MyPubSub instance will inherit methods from StreamMixin. However, I need to specify the class from which to inherit, and I need to do this at the moment of instance creation.

In other words, when u create MyPubSub instance u pass the use_stream argument, and depending on this argument, instance will inherit methods from StreamMixin or from SocketMixin.

It might look something like this:

class MyPubSub(StreamMixin, SocketMixin):
    
    def __new__(cls, *args, **kwargs):
        use_stream = kwargs.get('use_stream')
        
        if use_stream:
            return StreamMixin.__new__(cls)
        else:
            return SocketMixin.__new__(cls)

Or directly changing mro:

class MyPubSub(StreamMixin, SocketMixin):
    
    def __init__(self, use_stream):
        if use_stream:
            self.__mro__ = (MyPubSub, StreamMixin)
        else:
            self.__mro__ = (MyPubSub, SocketMixin)

Obviously it doesn’t work like that.

Maybe in some metaclass way?
Its even possible?

UPD:
Inheritance is not critical, the main goal to create the following behavior for MyPubSub:
It has a set of methods described in Mixins (Stream or Socket), and depending on the parametr use_stream it using the Stream methods or Socket methods, but solution must be “beautiful” - without checking a use_stream flag in every method, or getattribute and etc.

Critical is “Working class” must be MyPubSub

P.S.

  • Class structure can’t be changed to “OOP pretty style”
  • Please do not suggest getattribute solution
  • Please do not suggest target changing methods like self.publish = SocketMixin.publish solution
    I need kind of “beautiful” solution.

The beautiful solution is “don’t”. Why does MyPubSub need to be a class? Can’t it be a function that dynamically selects between the classes (or two different subclasses if you need to customize behavior)?

Whatever solution you are going to end up is going to cause issues one way or another, and probably shouldn’t be used anywhere near production.

Anyway, you will have to select a different class based on the use_stream argument since __mro__ belongs to the class, not the instances. For example you can use a pattern similar to pathlib where __new__ selects between PosixPath and WindowsPath based on the OS.

1 Like

I need kind of “beautiful” solution.

Favour composition over inheritance. Particularly in combination with dependency injection.

The PubSub class needs access to instances of Socket and Stream Mixins. But whatever it is, it doesn’t need to be, and therefore should not be, both a Socket and a Stream.

But if you do want to exploit Co-operative Multiple inheritance, and dance gracefully across the __mro__, then there is a super solution:

1 Like

MyPubSub must be a class.
I can’t use different classes.

Then the answer is “it’s impossible”. You can’t have multiple instances of the same class with different mros. (assuming no subclasses)

To help you find a solution that works for you we need more information.

You can use a class factory to dynamically create a subclass of both a base MyPubSub and a chosen mixin class:

class MyPubSub:
    ...  # custom behaviors

def my_pub_sub_factory(use_stream):
    mixin = StreamMixin if use_stream else SocketMixin
    class _MyPubSub(MyPubSub, mixin):
        pass
    return _MyPubSub

my_pub_sub_instance = my_pub_sub_factory(use_stream=True)()
assert isinstance(my_pub_sub_instance, MyPubSub)
2 Likes

Inheritance is not critical, the main goal to create the following behavior for MyPubSub:
It has a set of methods described in Mixins (Stream or Socket), and depending on the parametr use_stream it using the Stream methods or Socket methods, but solution must be “beautiful” - without checking a use_stream flag in every method, or getattribute and etc.

Why not get rid of use_stream and use Dependency Injection? Just pass in the instance of Stream or Socket (or anything else) to the constructor.

Thanks, thought about class dynamic creation. Unless there’s a mro/metaclass or other “super smart” solution here, I’ll do this

The dynamic approach is the smartest solution.

A metaclass defines the behavior of certain classes and their instances. Instead of doing this, you are simply subclassing Stream or Socket. The fact that these two subclasses will have the same name might be the source of confusion.

I think the base is the implementation detail here, rather than the factory result, according to the design OP wants. And we can use the functional API, too.

class _MyPubSub:
    ...  # custom behaviors

def my_pub_sub_factory(use_stream):
    mixin = StreamMixin if use_stream else SocketMixin
    return type('MyPubSub', (_MyPubSub, mixin), {})

Then what exactly is wrong with:

All the conditional checking is kept inside __init__ and you don’t need to change the class’ __getattr__ or __getattribute__. You can use fancy getattr and __dict__ tricks inside __init__ to assign every method, but you don’t have to. (There really shouldn’t be a lot of methods anyway.)

I think your structure is fine. People will say “favour composition over inheritance”, and this is important, time-tested advice.[1] But other people use mixins, because they don’t want to do the delegation work, and there’s also something to be said for that.

The problem is that the choice of mixin is static when you create the class. (Of course, as Ben showed, in Python you can just create the class dynamically.) The other nice thing about delegation is that it’s explicit: you can choose which mixin is handling what responsibility.


  1. Personally, I hardly use inheritance at all any more - in fact I would say I don’t write as many classes as others would for the same problem. I was already leaning in the direction of Jack Diederich’s advice before that talk. One of the great things about Python is how it enables blending multiple styles. For me, adding some functional-programming idioms lets me feel like my object-oriented programming is really object-oriented, rather than class-oriented. ↩︎

1 Like