Home > Blockchain >  Inline definition of static method in Python
Inline definition of static method in Python

Time:10-12

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

CodePudding user response:

You need tell Python that those are static methods with something like this:

class analysis1(object):

    from modules.analysis1 import (design, analyse, convert_inputs)

    get_inputs_from_raw = staticmethod(convert_inputs.convert_from_raw)    get_analysis = analyse.analyse_single_condition_offdesign
    test_condition = staticmethod(analyse.test_condition)

and then call them statically like analysis1.get_inputs_from_raw() and Python would not add self as a parameter.

Example:

Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def myfunc(name):
...     print(f'name: {name}')
... 
>>> myfunc('John')
name: John
>>> class myclass:
...     f = staticmethod(myfunc)
... 
>>> myclass.f('Lisa')
name: Lisa
  • Related