ProgrammationDéveloppeur Python middle/senior

Parlez-nous du fonctionnement et de l'utilisation des fonctions partial et partialmethod du module functools. Quelle est la différence entre elles, pourquoi les appliquer lors de la conception du code et quelles sont les particularités de leur utilisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

La fonction partial est apparue dans la bibliothèque standard de Python (module functools), à partir de Python 2.5, pour mettre en œuvre le motif de currying et l'application partielle des arguments à une fonction. partialmethod a été introduit avec Python 3.4 pour une application similaire dans les méthodes de classe.

Problème

Dans de nombreux projets réels, il est souvent nécessaire de fixer une partie des arguments d'une fonction pour obtenir une nouvelle fonction avec des valeurs "mémorisées" de certains arguments. Cela est pratique pour les callbacks, les modèles de passage d'arguments, ou pour améliorer la lisibilité du code, en particulier dans la programmation fonctionnelle et lors de l'utilisation d'API qui attendent un certain style de fonctions.

Solution

La fonction partial retourne un nouvel objet-fonction dans lequel certaines des valeurs d'arguments ou d'arguments clés passés sont "fixées". partialmethod met en œuvre la même approche pour les méthodes de classe, en prenant correctement en charge la mécanique de transmission de self/cls.

Exemple de code :

from functools import partial, partialmethod def power(base, exponent): return base ** exponent # Fixons l'exposant = 2 square = partial(power, exponent=2) print(square(5)) # 25 class Math: def power(self, base, exponent): return base ** exponent square = partialmethod(power, exponent=2) m = Math() print(m.square(5)) # 25

Caractéristiques clés :

  • partial crée une nouvelle fonction avec des valeurs d'arguments partiellement fixées.
  • partialmethod est une construction similaire pour les méthodes de classe, fonctionnant correctement avec self/cls.
  • Elles améliorent la lisibilité, permettent de créer des "wrappers fonctionnels" et des callbacks avec des paramètres fixes.

Questions pièges.

Peut-on utiliser partial pour les méthodes de classe afin d'obtenir un équivalent de partialmethod ?

Non, partial ne gère pas correctement self/cls, lorsqu'il est appliqué à une méthode de classe, il ne fonctionne pas comme prévu : self ne sera pas inséré automatiquement.

class C: def m(self, x, y): return x + y wrong = partial(m, y=2) # Incorrect !

Lors de l'appel c.wrong(5), une TypeError sera levée, car self ne sera pas transmis automatiquement.

L'objet créé par partial est-il une fonction ?

Oui, le résultat de partial est un objet qui se comporte comme une fonction, supporte l'appel, le nom, la docstring, etc., mais ce n'est pas une fonction ordinaire, c'est un objet de la classe partial, implémentant le protocole call.

from functools import partial f = partial(pow, 2) print(type(f)) # <class 'functools.partial'>

Partial fonctionne-t-il avec des arguments par défaut de la fonction ?

Oui, partial peut "réécrire" les valeurs par défaut dans la fonction interne ; si des arguments sont passés à partial, ceux-ci ont la priorité sur les valeurs par défaut de la fonction elle-même. Cependant, il est important de ne pas dupliquer les valeurs, sinon il y aura une erreur.

Erreurs typiques et anti-patrons

  • Appliquer partial aux méthodes liées, en espérant obtenir un self correct — cela ne fonctionne pas, il faut utiliser partialmethod.
  • Passer des valeurs fixes d'arguments trop tard ou incorrectes — cela entraînera une erreur lors de l'appel.
  • Embrouiller la structure du code en abusant de partial pour des fonctions triviales.

Exemple de la vie quotidienne

Cas négatif

Un développeur utilise partial pour une méthode de classe au lieu de partialmethod :

class MyClass: def f(self, x, y): ... squared = partial(f, y=2) ... obj = MyClass() obj.squared(5) # TypeError: missing 1 required positional argument: 'self'

Avantages :

  • Le code semble concis.

Inconvénients :

  • Ne gère pas la "magie" de la transmission de self.
  • L'erreur apparaît uniquement à l'exécution.

Cas positif

Au lieu de cela, on utilise partialmethod :

from functools import partialmethod class MyClass: def f(self, x, y): return x + y squared = partialmethod(f, y=2) obj = MyClass() print(obj.squared(5)) # 7

Avantages :

  • Fonctionne comme prévu.
  • Le code est lisible et évolutif.

Inconvénients :

  • Nécessite une connaissance des possibilités de partialmethod (pas pour les débutants).