Home > Blockchain >  Python: multiple inheritance and properties
Python: multiple inheritance and properties

Time:03-30

I've been working on a client/server TCP application of which here's a rough outline:

config_type = namedtuple('config', ['host', 'port'])


class Server:

    def __init__(self, config: config_type):
        self.config = config


class Client:

    def __init__(self, config: config_type):
        self.config = config

Now I'm expanding this architecture to other computers, so I'm implementing a 'slave' server that will run on the other hosts and distribute messages from the master to the clients on that host, so I wrote something like this:

class SlaveServer(Server, Client):

    def __init__(self, master_server_config: config_type, slave_server_config: config_type):
        Client.__init__(self, master_server_config)
        Server.__init__(self, slave_server_config)

However, since my Client and Server classes define a property called config it appears that the last init call wins:

master = config_type('localhost', 4342)
slave_config = config_type('localhost', 6555)

slave = SlaveServer(master, slave_config)

print(f'master={master}')
print(f'slave_config={slave_config}')
print(f'slave.config={slave.config}')

Outputs:

master=config(host='localhost', port=4342)
slave_config=config(host='localhost', port=6555)
slave.config=config(host='localhost', port=6555)

Which obviously will break things.

How is multiple inheritance supposed to be used in this kind of situation? Am I expected to make sure myself that conflicting property names do not exist? Perhaps in this case multiple inheritance is a bad idea and I should change SlaveServer so it doesn't use inheritance, and instead just has two properties, server and client.

CodePudding user response:

If you own all the code in there, you can use the name-mangling feature of Python: Just prefix the attributes and methods that would conflict with two __ in their name, and do nto try to touch those on the child classes (just on the very class where they are defined).

The double __ prefix triggers a build-time feature that changes the name of the attribute to include the class where they are defined as a prefix, in a transparent way - therefore both Server and Client can each see their own __config attribute. And if you try to reach for .__config on ClientServer you will get an AttributeError- but you can try and access each of them by using the mangled names:

from collections import namedtuple


config_type = namedtuple('config', ['host', 'port'])


class Server:

    def __init__(self, config: config_type):
        self.__config = config


class Client:

    def __init__(self, config: config_type):
        self.__config = config


class SlaveServer(Server, Client):

    def __init__(self, master_server_config: config_type, slave_server_config: config_type):
        Client.__init__(self, master_server_config)
        Server.__init__(self, slave_server_config)

And upon instantiating this on the REPL we can verify both attributes are saved:


In [65]: a = SlaveServer((0,0), (1,1))

In [67]: print(a._Server__config)
(1, 1)

In [68]: print(a._Client__config)
(0, 0)        

disclaimer: I am not judging your architecture, just pointing you to a solution built-in the language for this kind of attribute access in multiple inheritance. Even without seeing more of your code, this seems a text-book clear example where composition should be used over inheritance,.

  • Related