2017-01-05 3 views
0

Supposons que j'ai deux classes ...Comment une fonction peut-elle prendre ** kwargs comme arguments et les distribuer entre différentes classes?

class A: 

    def __init__(self, *args, arg1="default1", arg2="default2"): 
     # Initialise class A 


class B: 

    def __init__(self, arg3="default3", arg4="default4"): 
     # Initialise class B 

Chaque classe a ses propres arguments de mot-clé, et on a des arguments de position.

Supposons maintenant il y a une fonction qui crée une instance de chacune de ces classes, en utilisant ses propres arguments pour le faire:

def make_objs(*args, arg1="default1", arg2="default2", arg3="default3", arg4="default4"): 
    objA = ClassA(*args, arg1=arg1, arg2=arg2) 
    objB = ClassB(arg3=arg3, arg4=arg4) 

Ici, j'allouer manuellement les arguments de mots-clés que la fonction reçue à la classe correcte . C'est un peu fastidieux cependant - je dois dupliquer les mots-clés dans la définition de la fonction, et changer les classes signifiera changer les arguments de la fonction.

Idéalement, je ferais quelque chose comme ceci:

def make_objs(*args, **kwargs): 
    objA = ClassA(*args, **kwargs) 
    objB = ClassB(**kwargs) 

où chaque classe prendrait tous les arguments-clés et extraire uniquement ceux qui sont pertinents pour elle. Ce n'est pas ce que ferait le code ci-dessus bien sûr, il va lancer une exception car ClassA ne s'attend pas à un argument appelé arg3.

Y a-t-il un moyen de le faire? Une certaine façon de faire la fonction prend **kwargs comme argument et détermine quels arguments peuvent aller à quelle classe?

+0

Que diriez-vous des arguments '' constructor' dans la classe A' vous ajoutez 'et' * arg' ** kwarg' après 'arg1' et' arg2'? Il en va de même pour la «classe B». –

+0

Question: Quand allez-vous déballer '* args' dans' classA'? Pouvez-vous s'il vous plaît fournir la définition complète de votre 'constructor' pour' classA'? –

+0

La fonction est actuellement: https://github.com/samirelanduk/quickplots/blob/2.0/quickplots/quick.py#L4 –

Répondre

0

Si vous ajoutez *arg et **kwargs dans __init__ méthode de vos classes, vous pouvez atteindre le comportement que vous attendez. Comme dans le exaple ci-dessous:

class A(object): 

    def __init__(self, a, b, *arg, **kwargs): 
     self.a = a 
     self.b = b 

class B(object): 

    def __init__(self, c, d, *arg, **kwargs): 
     self.c = c 
     self.d = d 

def make_objs(**kwargs): 
    objA = A(**kwargs) 
    objB = B(**kwargs) 

make_objs(a='apple', b='ball', c='charlie', d='delta') 

Mais la mise en garde est que si vous imprimez objA.c et objA.d il retournera charlie et delta comme passé dans les paramètres make_objs.

+0

Désolé, j'aurais dû préciser que j'utilise déjà * args pour classA. La classe __init__ de classA est '(self, * args, ** kwargs)' et la fonction prend '(* args, ** kwargs)'. –

+0

Pouvez-vous mettre à jour votre question à nouveau avec cette information? Merci. Aussi pouvez-vous être plus spécifique à ce que vous attendiez de réaliser. Je ne suis peut-être pas clair sur votre question. –

+0

Merci, je viens de le faire. –

0
class A: 
    def __init__(self, *args, a1 = 'd1', a2 = 'd2'): 
     self.a1 = a1 
     self.a2 = a2 
class B: 
    def __init__(self, a3 = 'd3', a4 = 'd4'): 
     self.a3 = a3 
     self.a4 = a4 

Utilisez inpect.signature pour obtenir les signatures d'appel puis filtre kwargs avant de créer les objets.

from inspect import signature 
from inspect import Parameter 

sig_a = signature(A) 
sig_b = signature(B) 

def f(*args, **kwargs): 
    d1 = {} 
    d2 = {} 
    for k, v in kwargs.items(): 
     try: 
      if sig_a.parameters[k].kind in (Parameter.KEYWORD_ONLY, 
              Parameter.POSITIONAL_OR_KEYWORD, 
              Parameter.VAR_KEYWORD): 
       d1[k] = v 
     except KeyError: 
      pass 
     try: 
      if sig_b.parameters[k].kind in (Parameter.KEYWORD_ONLY, 
              Parameter.POSITIONAL_OR_KEYWORD, 
              Parameter.VAR_KEYWORD): 
       d2[k] = v 
     except KeyError: 
      pass 
    return (A(args, **d1), B(**d2)) 

d = {'a1':1, 'a2':2, 'a3':3, 'a4':4} 
x, y = f(2, **d) 

>>> x.a1 
1 
>>> x.a2 
2 
>>> y.a3 
3 
>>> y.a4 
4 
>>> 

Les contrôles pour voir si le paramètre est un paramètre mot-clé peut être surpuissant.

1

Pour éviter toute saisie répétitive, vous pouvez créer une fonction utilitaire qui utilise le module inspect pour examiner la séquence d'appel des fonctions/méthodes impliquées.

Voici un exemple exécutable de l'appliquer à votre code. La fonction d'utilité est celle du nom get_kwarg_names:

from inspect import signature, Parameter 

class ClassA: 
    def __init__(self, *args, arg1="default1", arg2="default2"): 
     print('ClassA.__init__(): ' 
       '*args={}, arg1={!r}, arg2={!r}'.format(args, arg1, arg2)) 

class ClassB: 
    def __init__(self, arg3="default3", arg4="default4"): 
     print('ClassB.__init__(): arg3={!r}, arg4={!r}'.format(arg3, arg4)) 

def get_kwarg_names(function): 
    """ Return a list of keyword argument names function accepts. """ 
    sig = signature(function) 
    keywords = [] 
    for param in sig.parameters.values(): 
     if(param.kind == Parameter.KEYWORD_ONLY or 
      (param.kind == Parameter.POSITIONAL_OR_KEYWORD and 
       param.default != Parameter.empty)): 
      keywords.append(param.name) 
    return keywords 

# Sample usage of utility function above. 
def make_objs(*args, arg1="default1", arg2="default2", 
        arg3="default3", arg4="default4"): 

    local_namespace = locals() 
    classA_kwargs = {keyword: local_namespace[keyword] 
         for keyword in get_kwarg_names(ClassA.__init__)} 
    objA = ClassA(*args, **classA_kwargs) 

    classB_kwargs = {keyword: local_namespace[keyword] 
         for keyword in get_kwarg_names(ClassB.__init__)} 
    objB = ClassB(**classB_kwargs) 

make_objs(1, 2, arg1="val1", arg2="val2", arg4="val4") 

Sortie:

ClassA.__init__(): *args=(1, 2), arg1='val1', arg2='val2' 
ClassB.__init__(): arg3='default3', arg4='val4' 
+0

J'aime mieux ça, j'ai été tenté d'en faire une fonction qui renvoyait des paramètres valides mais qui ne fonctionnait pas. – wwii

+0

Bien. J'ai essayé de faire quelque chose d'assez générique afin qu'il puisse être facilement modifié si nécessaire et réutilisé si désiré. Cependant, notez qu'il exige que les arguments du mot clé soient tous uniques. Si ma réponse résout votre problème, veuillez envisager de l'accepter. – martineau