Search code examples
pythonclass-methodclass-attribute

How to reference a class method in a class attribute?


In a package, I have isolated a set of functions/variables that work altogether in a class. As these functions did not require a specific context, they became class methods. They may use some external functions, here represented by the save_c function. But I can't get the following code to work:

def save_c(v):
    print("c :" + v)


class Writer(object):

    @classmethod
    def save(cls, t, v):
        cls.writers[t](v)


    @classmethod
    def save_a(cls, v):
        print("a :" + v)


    @classmethod
    def save_b(cls, v):
        print("b :" + v)

    writers = [save_a, save_b, save_c]


if __name__ == "__main__":
    Writer.save(1, [1, 2, 3])

Previous code results into the following error:

TypeError: 'classmethod' object is not callable

Does anybody know if what I'm trying to do is reachable? Or am I obliged to use regular methods and instanciate a Writer wherever I need to?


Solution

  • You could also make it work by adding a wrapper class that is smarter about what calling whatever it's dealing with.

    Since you mentioned in a comment that you're going to need to make the call to one of thewritersmultiple places, I've updated my answer to address this by adding a nested wrapper class to encapsulate the logic of calling the various callable types in one spot. This is a classic example of using OOP to apply the DRY principle.

    def save_c(v):
        print("c :" + v)
    
    class Writer(object):
        @classmethod
        def save(cls, t, v):
            cls.writers[t](cls, v)
    
        @classmethod
        def save_a(cls, v):
            print("a :" + v)
    
        @staticmethod
        def save_b(v):
            print("b :" + v)
    
        def _wrapper(writer):
            if isinstance(writer, classmethod):
                return type('ClassmethodWrapper', (object,), {'__call__':
                            lambda self, cls, v: writer.__func__(cls, str(v))})()
            elif isinstance(writer, staticmethod):
                return type('StaticmethodWrapper', (object,), {'__call__':
                            lambda self, cls, v: writer.__func__(str(v))})()
            else:
                return type('VanillaWrapper', (object,), {'__call__':
                            lambda self, cls, v: writer(str(v))})()
    
        writers = list(map(_wrapper, (save_a, save_b, save_c)))
    
    if __name__ == "__main__":
        Writer.save(0, [1, 2, 3])  # -> a :[1, 2, 3]
        Writer.save(1, [4, 5, 6])  # -> b :[4, 5, 6]
        Writer.save(2, [7, 8, 9])  # -> c :[7, 8, 9]