Search code examples
python-3.xsuperclass

Python super class with 2 inheritances calling function


I am trying to wrap a client rest api with its websocket like the following:

class Client(RequestClient, SubscriptionClient):
    def __init__(self, api_key, secret_key):
        super().__init__(api_key=api_key, secret_key=secret_key)

    def subscribe_event(self, callback, error_handler=None):
        super().subscribe_event(callback, error_handler)

def callback(msg):
    print(msg)

def error(e):
    print(e.error_code + e.error_message)

So when I call directly the SubsciptionClient and do:

client = SubscriptionClient(api_key=api_key, secret_key=secret_key)
client.subscribe_event(callback, error)

I have no problem.

But when I do

client = Client(api_key=api_key, secret_key=secret_key)
client.subscribe_event(callback, error)

I get the error:

AttributeError: 'Client' object has no attribute 'websocket_request_impl'

Under the hood (removing init function...)

class SubscriptionClient():
    def __init__(self, **kwargs):
        api_key = None
        secret_key = None
        if "api_key" in kwargs:
            api_key = kwargs["api_key"]
        if "secret_key" in kwargs:
            secret_key = kwargs["secret_key"]
        self.websocket_request_impl = WebsocketRequest(api_key)

    def subscribe_event(callback, error):
        request = self.websocket_request_impl.subscribe_event(callback, error_handler)
        self.__create_connection(request)

class RequestClient(object):
    def __init__(self, **kwargs):
        api_key = None
        secret_key = None
        if "api_key" in kwargs:
            api_key = kwargs["api_key"]
        if "secret_key" in kwargs:
            secret_key = kwargs["secret_key"]

Any idea what I do wrong when initializing my super class please?


Solution

  • From the error output, it is evident that the __init__() method of RequestClient is not being called. Both SubscriptionClient and RequestClient are subclasses of object. You can verify this using:

    print(issubclass(RequestClient, object))
    print(issubclass(SubscriptionClient, object))
    
    # True
    # True
    

    You are dealing with what is called cooperative multiple-inheritance. The structure you have created is visualized as:

                object
                 /  \
                /    \     
    RequestClient    SubscriptionClient
                \    /
                 \  /
                Client
    

    To solve this, you need to call the __init__() method of the parent class inside the __init__() method of each subclass.

    class SubscriptionClient:
        def __init__(self, **kwargs):
            super().__init__()
    
    class RequestClient:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
    
    class Client(RequestClient, SubscriptionClient):
        def __init__(self, api_key, secret_key):
            super().__init__(api_key=api_key, secret_key=secret_key)
    

    Because RequestClient is listed before SubscriptionClient in the Client declaration, the keyword arguments will first be passed to RequestClient instead of SubscriptionClient.

    class Client(RequestClient, SubscriptionClient):