2017-06-27 5 views
3

J'essaie d'utiliser les arguments du modèle pour plus de commodité dans un appel de fonction (via les arguments dict et mot-clé) tout en surchargeant certains arguments.Python: dans les arguments d'appel de méthode, remplacer le mot-clé argument

Par exemple, si nous commençons par un mymod module contenant la ligne template_kvps = {'a': 1, 'b': 2}

je peux:

import mymod 

def func(**kwargs): 
    pass 

func(**mymod.template_kvps) 

alors je peux accéder à mon template_kvps au sein func(). Mais je veux être en mesure de passer une valeur différente pour a avec un minimum de frais généraux. Tout ce que je peux penser est de modifier le dictionnaire avant l'appel de la fonction: kvps = {**template_kvps, 'a': 3}; func(**kvps), mais c'est deux fois le nombre de lignes et j'utilise cette fonction plusieurs fois dans environ 1000 scripts de test.

Je voudrais idéalement redéfinir func afin que je puisse faire sth comme func(**mymod.template_kvps, a=3) mais comme il est, les erreurs Python avec quelque chose sur les paramètres répétés.

btw Je suis heureux d'envisager de changer le format du modèle kvps. Note ajoutée plus tard Il ya quelques bonnes réponses à ce que j'ai demandé (et j'ai accepté ce que je pense être la meilleure réponse pour ce que j'ai demandé), mais je pense que j'ai demandé une mauvaise chose. Je propose cela est un problème de conception, et si je veux des arguments par défaut comme cela, je pense qu'il serait préférable de créer une méthode d'emballage avec des arguments par défaut au lieu d'un dictionnaire de modèle, comme

def func_template(a=1, b=2): 
    func(a, b) 

func_template(a=3) 
+0

Je me demande pourquoi nous tirons tous nos cheveux sur une ligne de code supplémentaire. Je ne vois pas comment c'est un gâchis. –

+5

Comment fonctionne func (mymod.template_kvps) '? Ne devrait-il pas être 'func (** mymod.template_kvps)'? – Artyer

+0

@Artyer, oui, mon erreur, corrigé – joelb

Répondre

2

Vous pouvez utiliser le type intégré dict à cette fin. Il accepte un autre dict comme argument et des paires clé-valeur supplémentaires comme arguments mot-clé (qui ont la priorité sur les valeurs de l'autre dict).

Ainsi, vous pouvez créer un dictionnaire mis à jour via dict(template_vars, a=1).

Vous pouvez développer cette dict en tant qu'arguments de mot clé: func(**dict(...)).Comme cela, il n'y a pas besoin de changer la signature de votre fonction et vous pouvez mettre à jour/ajouter autant de paires clé-valeur que vous le souhaitez.

0

Vous pourriez avoir une fonction qui met à jour les valeurs que vous voulez changer. (Sous la direction de sorte que le dictionnaire d'origine ne soit pas modifiée.)

import mymod 

def replace(dict1, dict2): 
    ans = dict1.copy() 
    ans.update(dict2) 
    return ans 

def func(**kwargs): 
    pass 

func(replace(mymod.template_kvps, {'a':3})) 
+1

'replace = dict.update' fait exactement la même chose. – Artyer

+0

Merci, beaucoup plus simple. –

0

Cela devrait fonctionner

func(a=3, **{key: value for key, value in mymod.template_kvps.items() if key != 'a')}) 

Cependant, je vous suggère d'écrire une autre ligne de code, et non confuscating il.

0

Vous pouvez le faire en une ligne comme celle-ci:

func(**{**mymod.template_kvps, 'a': 3}) 

Mais cela pourrait ne pas être évident au premier coup d'œil, mais est aussi évident que ce que vous faisiez avant.

Ce que je suggère est d'avoir plusieurs modèles (par exemple template_kvps_without_a), mais cela dépendra de votre cas d'utilisation spécifique:

func(**mymod.template_kvps_without_a, a=3) 
1

Je considérerais des décorateurs de fonction car ceci maintient la syntaxe la plupart du temps comme vous l'avez demandé. La mise en œuvre ressemblerait à quelque chose comme:

def combine(func): 
    def wrapper(*args, **kwargs): 
    fargs = {} 
    if args and isinstance(args[0], dict): 
     fargs = args[0].copy() 
    fargs.update(kwargs) 
    return func(**fargs) 
    return wrapper 


@combine 
def funky(**kwargs): 
    for k,v in kwargs.iteritems(): 
    print "%s: %s" % (k, v) 

# All of these work as expected 
funky({'a': 1, 'b': 2}, a=3) 
funky({'a': 1, 'b': 2}) 
funky(a=3) 
0

Je change l'argument de modèle pour être un argument de position, avec les clés à écraser comme **kwargs.

def func(template_dict, **kwargs): 
    updated_dict = {**template_dict, **kwargs} 
    return updated_dict 

print(func({'a':1, 'b':2}, a=3)) # {'a': 3, 'b': 2} 

Notez que la syntaxe de double-déballage nécessite Python 3.5+. Sur les anciennes versions, utilisez:

updated_dict = template_dict.copy() 
updated_dict.update(kwargs)