Home > Net >  Is there a way of defining multiple methods with similar bodies?
Is there a way of defining multiple methods with similar bodies?

Time:06-06

I'm trying to write my own Python ANSI terminal colorizer, and eventialy came to the point where I want to define a bunch of public properties with similar body, so I want to ask: is there a way in Python to define multiple similar methods with slight difference between them?
Actual code I'm stuck with:

class ANSIColors:
    @classmethod
    def __wrap(cls, _code: str):
        return cls.PREFIX   _code   cls.POSTFIX

    @classproperty
    def reset(cls: Type['ANSIColors']):
        return cls.__wrap(cls.CODES['reset'])

    @classproperty
    def black(cls: Type['ANSIColors']):
        return cls.__wrap(cls.CODES['black'])
    ...

If it actually matters, here is the code for classproperty decorator:

def classproperty(func):
    return classmethod(property(func))

I will be happy to see answers with some Python-intended solutions, rather then code generation programs.

Edit 1: it will be great to preserve given properties names.

CodePudding user response:

I don't think you need (or really want) to use properties to do what you want to accomplish, which is good because doing so would require a lot of repetitive code if you have many entries in the class' CODES attribute (which I'm assuming is a dictionary mapping).

You could instead use __getattr__() to dynamically look up the strings associated with the names in the class' CODES attribute because then you wouldn't need to explicitly create a property for each of them. However in this case it needs to be applied it the class-of-the-class — in other words the class' metaclass.

The code below show how to define one that does this:

class ANSIColorsMeta(type):
    def __getattr__(cls, key):
        """Call (mangled) private class method __wrap() with the key's code."""
        return getattr(cls, f'_{cls.__name__}__wrap')(cls.CODES[key])


class ANSIColors(metaclass=ANSIColorsMeta):
    @classmethod
    def __wrap(cls, code: str):
        return cls.PREFIX   code   cls.POSTFIX

    PREFIX = '<prefix>'
    POSTFIX = '<postfix>'
    CODES = {'reset': '|reset|', 'black': '|black|'}


if __name__ == '__main__':

    print(ANSIColors.reset)  # -> <prefix>|reset|<postfix>
    print(ANSIColors.black)  # -> <prefix>|black|<postfix>
    print(ANSIColors.foobar)  # -> KeyError: 'foobar'

✶ It's important to also note that this could be made much faster by having the metaclass' __getattr__() assign the result of the lookup to an actual cls attribute, thereby avoiding the need to repeat the whole process if the same key is ever used again (because __getattr__() is only called when the default attribute access fails) effectively caching looked-up values and auto-optimizing itself based on how it's actually being used.

  • Related