Historique de la question:
En Python, tout est objet, et toute fonction est aussi un objet. Mécanisme a été mis en place pour permettre à une instance de classe de se comporter comme une fonction. Pour ce faire, une méthode magique spéciale call a été introduite, qui permet de rendre les objets appelables.
Problème:
Il arrive parfois qu'il faille transmettre un objet qui peut exécuter des actions lorsqu'il est appelé, par exemple, des fonctions d'état (stateful functions), des fermetures, des objets de gestion d'événements, etc. Le développement de l'architecture nécessite de comprendre comment mettre en œuvre correctement cette fonctionnalité.
Solution:
En implémentant la méthode call dans une classe, on peut rendre son instance appelable "comme une fonction". Cela permet de combiner les capacités des classes (encapsulation d'état, héritage, méthodes) et des fonctions (appelabilité). Cette approche est utilisée pour créer des objets de commande, des gestionnaires complexes, des wrappers, etc.
Exemple de code :
class Adder: def __init__(self, x): self.x = x def __call__(self, y): return self.x + y add5 = Adder(5) print(add5(10)) # Affichera 15
Caractéristiques clés :
La méthode call hérite-t-elle des attributs d'une fonction ordinaire tels que name et doc ?
Non, l'objet avec la méthode call n'aura pas l'attribut name (ou le prendra de la classe). Les métadonnées des fonctions ne sont pas conservées.
Un objet avec call est-il une véritable fonction ?
Non, c'est une instance de classe, pas une fonction. Il ne fait que mettre en œuvre un comportement "appelable". Par exemple, sa comparaison avec une fonction via isinstance(obj, types.FunctionType) retournera False.
Peut-on appliquer un décorateur à un objet avec call qui est destiné à une fonction ?
En général, de tels décorateurs attendent spécifiquement une fonction, pas un objet (par exemple, functools.lru_cache). L'utilisation peut entraîner des erreurs ou ne pas fonctionner du tout.
Avantages:
Cas négatif : Dans un projet, un logger a été implémenté via une classe avec call, pour stocker des configurations (niveau, nom de fichier). Cependant, il a été oublié que les fonctions de gestion de signaux attendaient spécifiquement une fonction, et des erreurs sont survenues lors de l'enregistrement du gestionnaire (l'objet n'est pas une fonction).
Avantages : configuration flexible du logger. Inconvénients : incompatibilité avec les interfaces attendues.
Cas positif : Dans un autre projet, une classe avec call a été utilisée pour créer des fonctions-décorateurs complexes, conservant des paramètres, ce qui a simplifié les tests.
Avantages : extensibilité, commodité. Inconvénients : plus de code par rapport à une fonction ou un lambda.