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,.