I'm writing some report generation code, where a class CoverageReporter
is doing all of the heavy lifting, with just a few attributes changing between different instances of CoverageReporter
. Let's say we want to alias
SubsystemCoverageReporter(*args, **kwargs)
to
CoverageReporter('subsystem', *args, **kwargs)
in a concise way. Currently, I accomplish it like so:
class SubsystemCoverageReporter(object):
def __init__(self, *args, **kwargs):
self.reporter = CoverageReporter('subsystem', *args, **kwargs)
def __getattr__(self, name):
return getattr(self.reporter, name)
Another one, subsystem 2, has almost exactly the same boilerplate, eg:
class SubsystemTwoCoverageReporter(object):
def __init__(self, *args, **kwargs):
self.reporter = CoverageReporter('subsystem2', *args, **kwargs)
def __getattr__(self, name):
return getattr(self.reporter, name)
Is there a more concise Pythonic way to express this? I'd prefer not to rely on inheritance, but if there's no better ways I'm open to that.
CodePudding user response:
Say you had your class CoverageReporter
like so:
class CoverageReporter:
def __init__(self, _type, _name):
self.type = _type
self.name = _name
def do_something(self):
print(f"{self.type}:{self.name} did something")
def do_something_else(self):
print(f"{self.type}:{self.name} did something else")
You could define a function which creates the CoverageReporter
and returns that object.
def SubsystemCoverageReporter(*args, **kwargs):
return CoverageReporter('subsystem', *args, **kwargs)
def SubsystemTwoCoverageReporter(*args, **kwargs):
return CoverageReporter('subsystem2', *args, **kwargs)
s1 = SubsystemCoverageReporter("Foo")
s2 = SubsystemTwoCoverageReporter("Bar")
s1.do_something() # subsystem:Foo did something
s2.do_something_else() # subsystem2:Bar did something else
Note that this isn't exactly equivalent to what you have, since SubsystemCoverageReporter
isn't a class, so you won't be able to inherit it. If you want that option available, then off the top of my head I think inheritance is the best solution you have.
class SubsystemCoverageReporter(CoverageReporter):
def __init__(self, *args, **kwargs):
super().__init__('subsystem', *args, **kwargs)
class SubsystemTwoCoverageReporter(CoverageReporter):
def __init__(self, *args, **kwargs):
super().__init__('subsystem2', *args, **kwargs)
s1 = SubsystemCoverageReporter("Foo")
s2 = SubsystemTwoCoverageReporter("Bar")
s1.do_something() # subsystem:Foo did something
s2.do_something_else() # subsystem2:Bar did something else
CodePudding user response:
If you want to avoid repeating yourself, use the same solution you always would, wrap the repeated code in a function and parameterize the parts that can change:
def make_klass(subsystem):
class SubsystemCoverageReporter(object):
def __init__(self, *args, **kwargs):
self.reporter = CoverageReporter(subsystem, *args, **kwargs)
def __getattr__(self, name):
return getattr(self.reporter, name)
return SubsystemCoverageReporter
SubsystemCoverageReporter = make_klass("subsystem")
SubsystemTwoCoverageReporter = make_klass("subsystem2")
Note, the classes you create will have the same value for their __name__
attribute. That is hopefully not important, but you can get around it, e.g.:
SubsystemCoverageReporter.name = "SubsystemCoverageReporter" SubsystemTwoCoverageReporter.name = "SubsystemTwoCoverageReporter"
Although that is ugly.
You could use the type
constructor directly, or if metaclasses are an issue, use types.new_class
instead and it will handle it correctly. But in the simplest case,
def make_klass(name, subsystem):
def __init__(self, *args, **kwargs):
self.reporter = CoverageReporter(subsystem, *args, **kwargs)
def __getattr__(self, name):
return getattr(self.reporter, name)
klass = type(name, (object,), dict(__init__=__init__, __getattr__=__getattr__))
return klass
SubsystemCoverageReporter = make_klass("SubsystemCoverageReporter", "subsystem")
SubsystemTwoCoverageReporter = make_klass("SubsystemTwoCoverageReporter", "subsytem2")
Although note, you are repeating yourself here a little.
But again, normally you shouldn't care about the __name__
attribute