web-dev-qa-db-fra.com

L'objet 'classmethod' n'est pas appelable

Ok donc j'ai ce code:

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

Quand j'exécute ceci, je continue à avoir l'erreur: 

 Exception TypeError: "l'objet 'classmethod' n'est pas appelable" 

Je suis incapable de comprendre ce qui ne va pas avec cela et j'apprécierais votre aide.

22
user1126425

Vous ne pouvez pas créer de références aux méthodes de classe tant que la classe n'a pas été définie. Vous devrez le sortir de la définition de la classe. Cependant, utiliser une carte globale des fonctions pour décider de ce qui sera exécuté est vraiment délicat. Si vous décrivez ce que vous essayez de faire, nous pourrions probablement suggérer une meilleure solution.

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')
16
Hugh Bothwell

J'ai découvert quelque chose ce soir qui pourrait être utile ici: Nous pouvons décompresser les objets magiques staticmethod et classmethod via: getattr(func, '__func__')

Comment ai-je trouvé cette information? En utilisant PyCharm de JetBrains (je ne connais pas d’autres IDE Python), j’ai visualisé le code source pour @staticmethod et @classmethod. Les deux classes définissent l'attribut __func__.

"Le reste est laissé comme un exercice pour le lecteur."

10
kevinarpe

Une méthode de classe enveloppe une fonction donnée avec un objet magique qui n'est pas réellement appelable. Il ne peut être appelé qu'en utilisant la syntaxe ClassName.method(args).

Je ne recommanderais pas que vous le fassiez de cette façon, envisagez plutôt de faire quelque chose comme ceci:

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 __== '__main__':
    sc = SomeClass()
    sc.run('Argumentus-Primus')
2
Antti Haapala

Ajoutez self en tant qu'argument pour chaque méthode de la classe.

Également

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

devrait ressembler à ceci:

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

et ne devrait pas être dans le corps de la classe.

0
ulu5

Voici une mauvaise façon de le faire:

def func3(cls,arg1):
    global funcList
    for func in funcList:
        if funcList[func]==True:
            eval(f'SomeClass.{func}')(arg1)

Ne fonctionne que si func est le nom de la fonction. Cela étant dit, ne pas utilisez cette méthode, car vous utilisez les entrées de l'utilisateur. Il serait très facile d'injecter du code désagréable dans l'appel. Cela étant dit, cela fonctionne.

0
Rob Rose

Toutes les autres réponses suggèrent d'ajouter du code en dehors de la définition de class SomeClass. Cela peut aller dans certains cas, mais dans mon cas, c'était très gênant. Je voulais vraiment garder le func_map dans la classe.

Je suggère l'approche suivante. Utilisez non pas une variable de classe, mais une méthode de classe supplémentaire:

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)

Bien sûr, vous devez modifier ce code pour qu’un nouveau dictionnaire ne soit pas construit chaque fois que vous appelez la méthode get_func_map. C'est facile, je ne me suis pas fait de garder l'exemple petit et clair.

Testé sur Python 3.6

0
lesnik

Vous devrez peut-être essayer une méthode statique.

@staticmethod
def function():...

Les méthodes statiques ne transmettent pas la classe en tant que premier argument implicite.

0
John