I want to have immutable types that can, ideally, sort out their own hashing and equality, but can be easily subclassed. I started off using namedtuple:
class Command(namedtuple('Command', 'cmd_string')):
def valid_msg(msg):
return True
def make_command(msg):
if self.valid_msg(msg):
return '%s:%s' % (self.cmd_string, msg)
else:
raise ValueError(INVALID_MSG)
…but this does not lend itself to subclassing. Directly subclassing this means that the name of the tuple remains the same (for printing… not such a big deal), but more importantly you can’t add fields:
class LimitedLengthCommand(Command):
# I want to have self.length! Where does it go?
def valid_msg(msg):
return len(msg) <= self.length
Simply creating another named tuple (as per the docs) means I don’t inherit any methods!
What is the simplest and easiest way to do something like this? I intend to have multiple subclasses of Command (eg. hex literals, 1-or-0, etc), but nothing complicated. Playing nice with multiple inheritance is not essential.
Here’s a metaclass to do what you want (I think). It works by storing the methods to be inherited in a dictionary and manually inserting them into the new classes dictionary. It also stores the attribute string that gets passed to the
namedtupleconstructor and merges that with the attribute string from the subclass. It then passes that tonamedtupleand returns a class that’s inherited from the resultingnamedtuplewith all appropriate methods in its dictionary. Because the metaclass is derived fromabc.ABCMeta, you get working type checking for free. Here’s how constructing a couple of classes looks:Here’s the metaclass:
And here’s some paltry test code.
Hope that helps. If you would prefer not to attach every method to every class that is supposed to inherit it, you could also provide a default
__getattr__that looks through the metaclasses dictionary and finds the appropriate method out of that. That would require somehow hardcoding the baseclass into method, probably using a closure.