Как ссылаться на метод класса в атрибуте класса?

В пакете я выделил набор функций / переменных, которые в целом работают в классе. Поскольку эти функции не требовали определенного контекста, они стали методами класса. Они могут использовать некоторые внешние функции, представленные здесь save_c функция. Но я не могу заставить работать следующий код:

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])

Предыдущий код приводит к следующей ошибке:

TypeError: 'classmethod' object is not callable

Кто-нибудь знает, достижимо ли то, что я пытаюсь сделать? Или я обязан использовать обычные методы и создавать Writer везде, где мне нужно?

3 ответа

Решение

Вы также можете заставить его работать, добавив класс-обёртку, который умнее будет называть то, с чем он имеет дело.

Поскольку вы упомянули в комментарии, что вам нужно будет позвонить одному изwritersВ нескольких местах я обновил свой ответ для решения этой проблемы, добавив вложенный класс-обертку, чтобы инкапсулировать логику вызова различных вызываемых типов в одном месте. Это классический пример использования ООП для применения принципа СУХОЙ.

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]

Похоже, вы хотели использовать @staticmethod скорее, чем @classmethod,

Для получения дополнительной информации см. В чем разница между @staticmethod и @classmethod в Python?

На самом деле, внутренний writers Атрибут должен ссылаться на __func__ атрибут методов класса. И да, нам нужно здесь, чтобы использовать staticmethod декораторы вместо classmethod те, чтобы уважать ту же самую подпись, используемую для внешних вызовов. Следующий код работает хорошо:

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


class Writer(object):

    @staticmethod
    def save(t, v):
        Writer.writers[t](str(v))


    @staticmethod
    def save_a(v):
        print("a :" + v)


    @staticmethod
    def save_b(v):
        print("b :" + v)

    writers = [save_a.__func__, save_b.__func__, save_c]


if __name__ == "__main__":
    Writer.save(0, [1, 2, 3])
    Writer.save(1, [1, 2, 3])
    Writer.save(2, [1, 2, 3])
Другие вопросы по тегам