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.
1 Answer 1
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,.
1 Comment
Explore related questions
See similar questions with these tags.
SlaveServercontain an instance ofClientand an instance ofServer. Why don't you just have the remote clients contact the master server directly? What do you gain by inserting a go-between?