I'm wrapping a Python module consisting of lots of files in lots of directories into a class interface. I'm trying not to edit the existing code too much, to avoid upsetting established routines that others use to interface with it.
In order to avoid having to deal with relative imports, I'm importing all relevant modules when importing the class (which I can do from a fixed working directory, and then forget about paths). I can then address some of the existing functions like this:
class analysis1(object):
from modules.analysis1 import (design, analyse, convert_inputs)
@staticmethod
def get_inputs_converted(inputs_raw):
return(convert_inputs.convert_from_raw(inputs_raw))
def __init__(self, design_vars, conditions):
self.data = self.design.find_optimum(design_vars, conditions)
def get_performance(self, condition):
if not self.analyse.test_condition(condition):
raise(ValueError)
return(self.analyse.analyse_single_condition_offdesign(self.data, condition))
Fine. But this means some pretty long lines, and someone using my class interface does not see the docstrings on the original functions. However, since the original code is simply functions (although they do pass a lot of the same stuff between each other...), I should be able to simply "pipe" them through as staticmethods, like this:
class analysis1(object):
from modules.analysis1 import (design, analyse, convert_inputs)
get_inputs_from_raw = convert_inputs.convert_from_raw
test_condition = analyse.test_condition
get_analysis = analyse.analyse_single_condition_offdesign
def __init__(self, design_vars, conditions):
self.data = self.design.find_optimum(design_vars, conditions)
def get_performance(self, condition):
if not test_condition(condition):
raise(ValueError)
return(self.get_analysis(self.data, condition))
That doesn't just look more compact, it also forwards the entire function, including docstrings and autocomplete capability. Although I probably should not make everything available that way, it makes a lot of sense to do it with at least the bits that are frequently used (and the bits that would be genuinely useful as static methods anyway). It also allows me to implement a class method that instantiates the whole thing from raw input, for example:
@classmethod
def from_raw(cls, raw_input):
return(cls(cls.get_inputs_from_raw(raw_input))
That would still be possible otherwise, but particularly if several "forwarded" functions are involved, it can make the code a lot cleaner.
However:
While I can address those functions totally fine from the outside of the class (e.g. analysis1.get_inputs_from_raw(), and within class methods (cls.get_inputs_from_raw()), whenever I call them from inside or outside of an instance (self.get_inputs_from_raw(), or instance.get_inputs_from_raw()), Python auto-inserts self as the first argument.
Question:
Is there a way for me to pipe these functions through as properly-defined static methods, without having to use the @staticmethod decorator in combination with a complete function header (including docstrings and everything), which is what I wanted to avoid in the first place.
Ideally, I'd like to apply the effect of the decorator to the assignments I am using in the second version of the example code.
Because someone will ask:
No, my actual code looks a bit different, and I am aware that static methods are often worse than just plain standalone functions. But in this case, I have multiple modules with similar/analogous functions in them, and I want to make clear which group of functionality a method belongs to, by assigning it to package.analysis1, package.analysis2 and so forth. Also, the current version of the code makes all functions available for direct access anyway, so it's not like I'm exposing a lot of code that should better be kept inside an instance to avoid confusing users...