En gros, partial
faites quelque chose comme ça (à part le support des arguments de mot-clé, etc.):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
Ainsi, en appelant, partial(sum2, 4)
vous créez une nouvelle fonction (un appelable, pour être précis) qui se comporte comme sum2
, mais qui a un argument de position en moins. Cet argument manquant est toujours remplacé par 4
, de sorte quepartial(sum2, 4)(2) == sum2(4, 2)
Quant à savoir pourquoi il est nécessaire, il existe une variété de cas. Juste pour un, supposons que vous deviez passer une fonction quelque part où elle devrait avoir 2 arguments:
class EventNotifier(object):
def __init__(self):
self._listeners = []
def add_listener(self, callback):
''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...
def notify(self, event, *params):
for f in self._listeners:
f(event, params)
Mais une fonction que vous avez déjà a besoin d'accéder à un troisième context
objet pour faire son travail:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
Donc, il existe plusieurs solutions:
Un objet personnalisé:
class Listener(object):
def __init__(self, context):
self._context = context
def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)
notifier.add_listener(Listener(context))
Lambda:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
Avec partiels:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
De ces trois, partial
c'est le plus court et le plus rapide. (Pour un cas plus complexe, vous voudrez peut-être un objet personnalisé).
extra_args
variableextra_args
est quelque chose qui est passé par l'appelant partiel, dans l'exemple avecp = partial(func, 1); f(2, 3, 4)
lui(2, 3, 4)
.callback
etmy_callback
partiels sont incroyablement utiles.
Par exemple, dans une séquence d'appels de fonction en "pipe-line" (dans laquelle la valeur retournée d'une fonction est l'argument passé à la suivante).
Parfois, une fonction dans un tel pipeline nécessite un seul argument , mais la fonction immédiatement en amont de celle-ci renvoie deux valeurs .
Dans ce scénario,
functools.partial
peut vous permettre de conserver ce pipeline de fonctions intact.Voici un exemple spécifique et isolé: supposons que vous souhaitiez trier certaines données en fonction de la distance de chaque point de données par rapport à une cible:
Pour trier ces données en fonction de la distance de la cible, ce que vous voudriez bien sûr faire est ceci:
mais vous ne pouvez pas - le paramètre clé de la méthode de tri n'accepte que les fonctions qui prennent un seul argument.
alors réécrivez
euclid_dist
comme une fonction prenant un seul paramètre:p_euclid_dist
accepte désormais un seul argument,vous pouvez maintenant trier vos données en passant la fonction partielle pour l'argument clé de la méthode de tri:
Ou par exemple, l'un des arguments de la fonction change dans une boucle externe mais est fixé lors de l'itération dans la boucle interne. En utilisant un partiel, vous n'avez pas à passer le paramètre supplémentaire lors de l'itération de la boucle interne, car la fonction modifiée (partielle) n'en a pas besoin.
créer une fonction partielle (en utilisant le mot-clé arg)
vous pouvez également créer une fonction partielle avec un argument positionnel
mais cela lancera (par exemple, créer un partiel avec un argument mot-clé puis appeler en utilisant des arguments positionnels)
un autre cas d'utilisation: écrire du code distribué en utilisant la
multiprocessing
bibliothèque de python . Un pool de processus est créé à l'aide de la méthode Pool:Pool
a une méthode de carte, mais cela ne prend qu'un seul itérable, donc si vous avez besoin de passer une fonction avec une liste de paramètres plus longue, redéfinissez la fonction comme partielle, pour corriger tout sauf un:la source
réponse courte,
partial
donne des valeurs par défaut aux paramètres d'une fonction qui autrement n'auraient pas de valeurs par défaut.la source
partial
et ainsi de suiteLes partiels peuvent être utilisés pour créer de nouvelles fonctions dérivées qui ont certains paramètres d'entrée pré-assignés
Pour voir une utilisation réelle des partiels, reportez-vous à ce très bon article de blog:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
Un simple , mais l'exemple de débutant propre du blog, couvre la façon dont on peut utiliser
partial
surre.search
pour rendre le code plus lisible.re.search
la signature de la méthode est:En appliquant,
partial
nous pouvons créer plusieurs versions de l'expression régulièresearch
en fonction de nos besoins, par exemple:Maintenant
is_spaced_apart
et ilis_grouped_together
y a deux nouvelles fonctions dérivées dere.search
qui ont l'pattern
argument appliqué (puisquepattern
c'est le premier argument dans lare.search
signature de la méthode).La signature de ces deux nouvelles fonctions (appelables) est:
Voici comment vous pouvez ensuite utiliser ces fonctions partielles sur du texte:
Vous pouvez consulter le lien ci - dessus pour obtenir une compréhension plus approfondie du sujet, car il couvre cet exemple spécifique et bien plus encore.
la source
is_spaced_apart = re.compile('[a-zA-Z]\s\=').search
? Si tel est le cas, y a-t-il une garantie que l'partial
idiome compile l'expression régulière pour une réutilisation plus rapide?À mon avis, c'est un moyen d'implémenter le curry en python.
Le résultat est 3 et 4.
la source
Il convient également de mentionner que lorsque la fonction partielle a passé une autre fonction où nous voulons "coder en dur" certains paramètres, cela devrait être le paramètre le plus à droite
mais si nous faisons de même, mais en changeant un paramètre à la place
il lancera une erreur, "TypeError: func () a plusieurs valeurs pour l'argument 'a'"
la source
prt=partial(func, 7)
Cette réponse est plus un exemple de code. Toutes les réponses ci-dessus donnent de bonnes explications sur les raisons pour lesquelles on devrait utiliser partial. Je vais donner mes observations et cas d'utilisation sur partial.
La sortie du code ci-dessus doit être:
Notez que dans l'exemple ci-dessus, un nouvel appelable a été renvoyé qui prendra le paramètre (c) comme argument. Notez que c'est également le dernier argument de la fonction.
La sortie du code ci-dessus est également:
Notez que * a été utilisé pour décompresser les arguments non-mot-clé et l'appelable renvoyé en termes d'argument qu'il peut prendre est le même que ci-dessus.
Une autre observation est la suivante: L' exemple ci - dessous démontre que partial renvoie un appelable qui prendra le paramètre non déclaré (a) comme argument.
La sortie du code ci-dessus doit être:
De même,
Au-dessus des impressions de code
J'ai dû l'utiliser lorsque j'utilisais la
Pool.map_async
méthode dumultiprocessing
module. Vous ne pouvez passer qu'un seul argument à la fonction de travail, donc j'ai dû l'utiliserpartial
pour que ma fonction de travail ressemble à une fonction appelable avec un seul argument d'entrée, mais en réalité ma fonction de travail avait plusieurs arguments d'entrée.la source