2017-10-04 8 views
0

Je travaille sur une fonction/méthode d'initialisation qui devrait vous avertir si les valeurs par défaut sont utilisées pour les kwargs. Je ne peux pas savoir comment faire et google ne m'a pas aidé non plus. Des solutions?Comment vérifier lors d'un appel de fonction si des arguments par défaut sont utilisés

Pour illustrer

class MyClass(): 
    def __init__(a=0,b=1): 
     ''' Should warn you if using default.''' 
     self._a = a 
     self._b = b 

devrait agir comme suit

MyClass(a=1) 
>>> 'warning: default b=1 is used. 

Pourquoi voudrait-on l'avertissement: Defualts sont là pour illustrer les valeurs typiques utilisées. Cependant, l'utilisateur final doit les définir et ne pas être paresseux (!). L'utilisateur final manipule yamls avec kwargs: informez l'utilisateur final s'il/elle bousille. Fondamentalement, je veux l'exigence (souple) des args avec tous les avantages de kwargs.

+1

Pourriez-vous expliquer pourquoi vous auriez besoin d'avertir sur les valeurs par défaut? Si elles sont par défaut, je pense que la fonction devrait fonctionner normalement sans avertissement. – noel

+1

Devrait-il y avoir un avertissement si quelqu'un appelle 'MyClass (0, 1)'? –

+0

Eh bien .. la sensibilisation de l'utilisateur est toujours une bonne chose, et si l'utilisateur sait qu'une valeur par défaut spécifique est utilisée si elle n'est pas spécifiquement définie semble extrêmement intéressant. Cela pourrait aussi être utile dans une situation où la deuxième valeur change, si elle n'est pas spécifiée, basée sur les valeurs du premier argument .. –

Répondre

1

Utilisation kwargs dans __init__ pour les mises en garde, puis une fonction dédiée initialize pour l'attribution des variables:

class MyClass(): 
    def __init__(self, **kwargs): 
     defaults = dict(a=0, b=1) 
     for key,val in defaults.items(): 
      if key not in kwargs: 
       print("warning: default {}={} is used.".format(key, val)) 
      kwargs[key]=val 

     self.initialize(**kwargs) 

    def initialize(self, a=0, b=1): 
     self._a = a 
     self._b = b 

MyClass() 
print('-'*50) 
MyClass(a=5) 
print('-'*50) 
MyClass(a=4,b=3) 

La sortie est:

warning: default b=1 is used. 
warning: default a=0 is used. 
-------------------------------------------------- 
warning: default b=1 is used. 
-------------------------------------------------- 
+0

Merci! Cela semble être une bonne approche générale. Un peu verbeux avec la double définition des valeurs par défaut mais c'est quelque chose que vous ne pouvez probablement pas faire dans le langage. – balletpiraat

3

Qu'est-ce que vous pourriez faire:

class MyClass(object): 
    def __init__(self, **kwargs): 
     if not 'b' in kwargs: 
      print "warning" 
     self._a = kwargs.get('a', 0) 
     self._b = kwargs.get('b', 1) 

Ce code utilise la fonction « get » d'un dictionnaire qui extrait la valeur stockée dans la clé (qui est le premier argument) et, si les clés n'existe pas , repli sur le deuxième argument qui est la valeur "par défaut".

Je ne sais pas si cela peut vous aider?

Python 2.7 "get" function for a dictionary


Mise à jour

Parce que la réponse de Thomas Kuhn était extrêmement intéressant, je voulais pousser encore plus loin:

class MyClass(object): 

    def __init__(self, **kwargs): 
     defaults = dict(a=0, b=1) 
     for key, value in defaults.iteritems(): 
      if not key in kwargs: 
       print "Warning: default {0}={1} is used.".format(key, value) 
      kwargs.setdefault(key, value) 
     self.__dict__.update(kwargs) 

m = MyClass() 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10) 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10, b=11) 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

De cette façon, vous pouvez avoir deux avantages principaux :

  • Votre liste d'attributs sera ajoutée directement dans la classe en tant que paramètre afin que vous puissiez y accéder.
  • La méthode setdefault extrait la valeur si la clé existe, et si ce n'est pas simplement ajoute la clé avec la valeur par défaut spécifiée.

Je comprends que ce code pourrait être extrêmement dangereux dans un environnement non contrôlé, de sorte que vous pouvez éventuellement fallback sur une structure plus complexe pour votre classe, en utilisant les méthodes:

  • __getattr__(self, attributename) pour extraire la valeur d'attribut basée sur le nom défini, de sorte que vous pouvez conserver toutes vos valeurs dans une variable locale comme self.classparameters ou quelque chose
  • __setattr__(self, attributename, attributevalue) pour définir la valeur.

Et ceci est le code:

class MyClass(object): 

    def __init__(self, **kwargs): 
     defaults = dict(a=0, b=1) 
     for key, value in defaults.iteritems(): 
      if not key in kwargs: 
       print "Warning: default {0}={1} is used.".format(key, value) 
      kwargs.setdefault(key, value) 
     self.__dict__.update(kwargs) 

    def __getattr__(self, parameter): 
     return self.__dict__.get(parameter) 

    def __setattr__(self, parameter, value): 
     self.__dict__.update({parameter: value}) 

m = MyClass() 
m.b = 11 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10) 
m.b = 101 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10, b=11) 
m.b = 1001 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

C'est quelque chose qui doit être portable avec un soin extrême parce que vous pouvez (et) ont des problèmes graves dans plusieurs situations.

Alerte: c'est pas une explication sur la façon d'utiliser __setattr__ et __getattr__, au lieu est un simple (et stupide) façon d'aborder le même problème avec de multiples solutions, donc si vous savez comment améliorer ce bit avec une façon plus intelligente d'utiliser ces méthodes magiques , de rien.


Une autre façon de mettre en œuvre ce processus, qui pourrait être plus bavard, mais vous permet d'avoir beaucoup de contrôle de ce qui se passe utilise le @property décorateur:

class MyClass(object): 

    def __init__(self, **kwargs): 
     self.classparam = dict(a=0, b=1) 
     for key, value in self.classparam.iteritems(): 
      if not key in kwargs: 
       print "Warning: default {0}={1} is used.".format(key, value) 
      kwargs.setdefault(key, value) 
     self.classparam.update(kwargs) 

    @property 
    def a(self): 
     return self.classparam.get('a') 

    @a.setter 
    def a(self, value): 
     self.classparam.update(a=value) 

    @property 
    def b(self): 
     return self.classparam.get('b') 

    @b.setter 
    def b(self, value): 
     self.classparam.update(b=value) 

m = MyClass() 
m.b = 10 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10) 
m.b = 100 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 

m = MyClass(a=10, b=11) 
m.b = 1000 
print "a: {0}; b: {1}".format(m.a, m.b) 
print "-"*30 
+0

J'aime cette solution! C'est très au point. Ce serait génial pour un petit nombre de kwargs. La réponse de Thomas Kühn fonctionnerait dans un cas plus général. – balletpiraat

+0

Merci pour cette suggestion inspirante. Je n'étais pas au courant de la méthode setdefault! Voir ma réponse pour ce que j'ai finalement trouvé. – balletpiraat

+0

@balletpiraat, de rien! En fait, vous avez inspiré mon avec quelques améliorations à mes contrôles de paramètres dans mon flux de travail :) –

0

Ce que je suis venu avec la suite - avec l'aide d'un collègue - est d'utiliser un attribut de classe pour stocker les paramètres par défaut:

À titre d'illustration:

class MySpecialClass(): 

    default_kwargs = dict(a=0,b=0) 

    def __init__(self,kwargs): 

     _kwargs = do_something(kwargs,self.default_kwargs) 

     self.a = _kwargs['a'] 
     ... some more unpacking ... 

instance = MySpecialClass({'a':0}) 
>>> some effect 

do_something() remplit les clés manquantes/avertissements etc. soulève

Pour moi, cela a tout ce que je veux:

  • explicite sur la valeur par défaut
  • simple, utilise pas de langage non évidente caractéristiques
  • déballer explicite - vous obtenez une erreur s'il y a encore quelque chose qui manque.
  • flexible/nettoyage via do_something()
  • plus?
+0

mmh ..vous pouvez toujours utiliser '** kwargs', car la façon dont vous l'utilisez n'est pas évidente, mais nécessite toujours qu'un paramètre soit passé au constructeur. Qu'est-ce qui se passe avec '** kwargs' à la place est une façon plus pythonique de laisser le constructeur (ou toute autre fonction ou méthode) pour avoir les paramètres passés comme:' MyClass (a = 12, b = 'asd') 'qui pourrait être une manière plus complète de travailler avec un objet. –