Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Pourquoi dans ce cas j’ai un AttributeError ?

+1 vote

Effectivement, je n’avais pas fait gaffe à ça. En fait, j’ai une erreur avec le code ci-après et je pensais que le problème était le même. Or il ne me semble pas.

def store_message(message):
    if not hasattr(store_message, 'assigned'):
        store_message.assigned = {}

    def assign(message_type):
        def wrapper(func):
            if message_type not in store_message.assigned:
                store_message.assigned[message_type] = func
            else:
                raise KeyError
            return func
        return wrapper

    store_message.assign = assign
    store_message.assigned[message['message_type']](message)

@store_message.assign('my_type')
def store_my_type(message):
    print('store_my_type is storing {}'.format(message))

store_message({'message_type': 'my_type'})
demandé 11-Mai-2016 par Ryzz (208 points)
edité 11-Mai-2016 par Ryzz

2 Réponses

+1 vote

A mon avis, il manque un 'return assign' à la fin de 'store_message()'.

répondu 11-Mai-2016 par bubulle (2,256 points)

Tu veux dire que store_message devrait renvoyer assign ? store_message n’est pas un décorateur, c’est assign le décorateur et elle renvoie bien wrapper.

Oups, j'ai dû lire trop vite, je suis d'accord avec toi.

+2 votes

Le problème, c'est que 'store_message.assign' n'existe pas avant le premier appel de 'store_message'.

Autre chose étrange, c'est que la 'sous'-fonction 'assign' ne dépend pas de l'attribut 'message' de 'store_message'. Il n'y a donc pas vraiment d'intérêt à la mettre là.
Par contre, 'assign' travaille sur 'store_message' et ton approche ne garantie pas que le nom store_message désigne toujours la fonction initiale...

Je te propose :

def message_storer():
    assigned = {}

    def store_message(message):
        assigned[message['message_type']](message)

    def assign(message_type):
        def wrapper(func):
            # je garde le corps de wrapper même si je ne comprends pas la logique
            # en particulier pourquoi KeyError si une clé existe déjà ?
            if message_type not in assigned:
                assigned[message_type] = func
            else:
                raise KeyError
            return func
        return wrapper

    store_message.assign = assign
    return store_message

store_message = message_storer()  # tu peux même créer plusieurs fonctions distinctes

@store_message.assign('my_type')
def store_my_type(message):
    print('store_my_type is storing {}'.format(message))

store_message({'message_type': 'my_type'})
répondu 13-Mai-2016 par bubulle (2,256 points)

Ça marche, mais je voulais justement éviter d’utiliser ça : store_message = message_storer() car du coup, autant créer un objet callable, ce sera tout aussi clair… maintenant, si c’est la seule solution allons-y !
Pour le KeyError, le but c’est de s’assurer qu’on ne va pas écraser une fonction préalablement enregistrée.

Tout à fait d'accord sur le fait qu'on puisse obtenir la même chose avec une approche plus objet.
Dire que qu'un objet callable serait plus clair dépend vraiment des habitudes de chacun. J'ai par exemple d'abord penser proposer une réponse OOP puis je me suis dit qu'il serait plus correct de rester au plus près du code initial.

Un avantage certain de l'objet sera naturellement de pouvoir lui ajouter des méthodes si besoin. A voir donc selon l'application finale.

Par plus clair, je veux dire qu’on est plus habitué à voir attachées des méthodes à un objet qu’à une fonction donc si au final on doit en plus avoir une sorte d’initialisation, autant prendre un objet ce qui sera plus «commun» (en plus en le faisant hériter de collections.Callable, on rends vraiment les choses explicites).

Par contre, je me demandais: dans le version utilisant une fonction, est-ce que cette dernière peut être considérée comme un singleton ?

Il me semblais qu’il y avait un article sur sametmax sur l’attachement de méthode à une fonction, mais je ne l’ai pas retrouvé (s’il existe).

...