объект 'classmethod' не вызывается


Итак, у меня есть такой код:

class SomeClass:
    @classmethod
    def func1(cls,arg1):
        #---Do Something---
    @classmethod
    def func2(cls,arg1):
        #---Do Something---

    # A 'function map' that has function name as its keys and the above function
    # objects as values
    func_map={'func1':func1,'func2':func2}

    @classmethod
    def func3(cls,arg1):
        # following is a dict(created by reading a config file) that
        # contains func names as keys and boolean as values that tells
        # the program whether or not to run that function
        global funcList
        for func in funcList:
            if funcList[func]==True:
                cls.func_map[func](arg1)        #TROUBLING PART!!!

    if _name__='main'
        SomeClass.func3('Argumentus-Primus')

Когда я запускаю это, я продолжаю получать ошибку:

Exception TypeError: "'classmethod' object is not callable"
Я не могу понять, что в этом плохого, и был бы признателен вам за помощь.
6 22

6 ответов:

Вы не можете создавать ссылки на classmethods, пока класс не определен. Вам придется удалить его из определения класса. Однако использование глобальной карты функций для определения того, что будет запущено, действительно неудобно. Если бы вы описали, что вы пытаетесь сделать с этим, мы, вероятно, могли бы предложить лучшее решение.

class SomeClass(object):
    @classmethod
    def func1(cls, arg1):
        print("Called func1({})".format(arg1))

    @classmethod
    def func2(cls, arg1):
        print("Call func2({})".format(arg1))

    @classmethod
    def func3(cls, arg1):
        for fnName,do in funcList.iteritems():
            if do:
                try:
                    cls.func_map[fnName](arg1)
                except KeyError:
                    print("Don't know function '{}'".format(fnName))

# can't create function map until class has been created
SomeClass.func_map = {
    'func1': SomeClass.func1,
    'func2': SomeClass.func2
}

if __name__=='__main__':
    funcList = {'func1':True, 'func2':False}
    SomeClass.func3('Argumentus-Primus')

Сегодня вечером я обнаружил кое-что, что будет полезно здесь: мы можем развернуть магию staticmethod и classmethod объекты через: getattr(func, '__func__')

Как я нашел эту информацию? Используя PyCharm JetBrains (я не знаю о других Python IDEs), я просмотрел исходный код для @staticmethod и @classmethod. Оба класса определяют атрибут __func__.

"остальное оставлено как упражнение для читателя."

Метод classmethod обертывает данную функцию магическим объектом, который на самом деле не может быть вызван. Его можно вызвать только с помощью синтаксиса ClassName.method(args).

Я бы не рекомендовал вам делать это таким образом, подумайте о том, чтобы сделать что-то вроде этого:

func_list = {
    'func1': True,
    'func2': False
}

class SomeClass(object):
    def do_func1(self, arg1):
        print("func1", arg1)

    def do_func2(self, arg1):
        print("func2", arg1)

    def run(self, arg1):
        for name, enabled in func_list.items():
            if enabled:
                the_method = getattr(self, 'do_' + name)
                the_method(arg1)


if __name__ == '__main__':
    sc = SomeClass()
    sc.run('Argumentus-Primus')

Добавьтеself в качестве аргумента для каждого метода в классе.

Также

    if _name__='main'
    SomeClass.func3('Argumentus-Primus')

Должно выглядеть так:

if __name__=='__main__':
    SomeClass.func3('Argumentus-Primus')

И не должны находиться в теле класса.

Возможно, вам потребуется попробовать статический метод.

@staticmethod
def function():...

Статические методы не передают класс в качестве неявного первого аргумента.

Все остальные ответы предполагают добавление некоторого кода вне определения class SomeClass. В некоторых случаях это может быть нормально, но в моем случае это было очень неудобно. Я действительно хотел сохранить func_map внутри класса.

Я предлагаю следующий подход. Используйте не переменную класса, А еще один classmethod:

class SomeClass:
    # ...

    @classmethod
    def get_func_map(cls):
        return {'func1': cls.func1, 'func2': cls.func2}

    @classmethod
    def func3(cls, arg1):
        # .....
        cls.get_func_map()[func_name](arg1)

Конечно, вы должны изменить этот код так, чтобы новый словарь не создавался каждый раз, когда вы вызываете метод get_func_map. Это легко, я не делал себе этого, чтобы держать пример маленьким и четкий.

Протестировано на python 3.6