2016-06-23 9 views
5

Ho una classe con un init che richiede molti argomenti: alcuni richiesti, altri no.Come ottenere __init __() per generare un'eccezione più utile invece di TypeError quando # di argomenti errati?

Se uno degli argomenti necessari non viene fornito, è possibile ottenere un errore TypeError, con un messaggio inutile come "richiede almeno 10 argomenti (14 dati)".

Mi piacerebbe avere una sottoclasse TypeError personalizzata che in realtà informa l'utente di quali argomenti mancano.

+1

Questi argomenti di parole chiave o posizionali? – zwol

+0

@zwol kwargs (che può essere fornito in posizione). –

+0

@zwol, detto questo ... tutti gli argomenti sono denominati argomenti. La funzione non dovrebbe importare se sono fornite come kwarg o posizionalmente, dovrebbe sapere quale manca in base a se stessa in entrambi i modi, no? –

risposta

5

Se si desidera utilizzare kwargs, è possibile impostare in init un elenco di argomenti richiesti e quindi verificare se sono presenti tutti i kwarg richiesti. Guarda il seguente esempio.

class Something(): 
    def __init__(self, **kwargs): 
     required_args = ['arg_1', 'arg_2'] 

     for arg in required_args: 
      if arg not in kwargs: 
       raise TypeError("Argument %s is required" % arg) 

obj = Something(arg_1=77, arg_3=2) 
+0

Mentre dovevo modificare questa risposta per essere un po 'più efficiente, e quindi avevo bisogno di riscriverlo per essere un po' più robusto e, a condizione che sotto, fosse più appropriato "accettare" questo ...Ma se hai intenzione di implementare questo, per favore vedi la mia risposta qui sotto. –

1

In Python 3, è possibile definire una funzione che richiede "argomenti con parole chiave obbligatori". Questo è più chiaro documentato in PEP 3102. Il messaggio di errore che si ottiene quando si omettono gli argomenti con parole chiave obbligatori include i nomi degli argomenti.

$ python3 
Python 3.5.2rc1 (default, Jun 13 2016, 09:33:26) 
[GCC 5.4.0 20160609] on linux 
Type "help", "copyright", "credits" or "license" for more information. 

>>> class X: 
... def __init__(self, *, foo, bar, baz): 
...  self.foo = foo 
...  self.bar = bar 
...  self.baz = baz 
... 
>>> a = X(foo=1,bar=2,baz=3) 
# no error 
>>> b = X(foo=1,bar=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() missing 1 required keyword-only argument: 'baz' 
>>> b = X(foo=1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() missing 2 required keyword-only arguments: 'bar' and 'baz' 

Tuttavia, questo non è compatibile con il codice che si aspetta di essere in grado di chiamare X() con posizionali argomenti, e il messaggio di errore che si ottiene è ancora quello che non ti piace:

>>> a = X(1,2,3) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() takes 1 positional argument but 4 were given 

Inoltre, questa funzione non è disponibile in qualsiasi versione di Python 2:

$ python 
Python 2.7.12rc1 (default, Jun 13 2016, 09:20:59) 
[GCC 5.4.0 20160609] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> class X(object): 
... def __init__(self, *, foo, bar, baz): 
    File "<stdin>", line 2 
    def __init__(self, *, foo, bar, baz): 
         ^
SyntaxError: invalid syntax 

Migliorare la diagnostica data per gli argomenti posizionali sarebbe probabilmente coinvolto l'hacking dell'interprete. Il team di sviluppo di Python potrebbe essere suscettibile alle patch; Prenderò in considerazione l'idea di inserirlo nella mailing list python-ideas.

+0

Veramente parteciperò a quell'elenco e lo suggerirò. Grazie per la raccomandazione –

0

Ho finito per utilizzare una variazione di Tomas Gonzalez's answer, riscrivendola in modo che supporti la combinazione di argomenti ordinati, argomenti di parole chiave e valori predefiniti esistenti. Questo ha supportato la nostra esigenza di scrivere codice che sia sempre retrocompatibile con il codice legacy. Per le implementazioni "greenfield", la risposta di Tomas è preferibile (e si farebbero sempre chiamate con kwargs). Per situazioni esistenti come la mia (o quelli in cui una combinazione di ordine richiesto e non richiesti argomenti possono essere necessari?), Considerare quanto segue:

def __init__(*args, **kwargs): 
    required_args = ['a', 'b'] 

    # to support legacy use that may included ordered args from previous iteration of this function 
    ordered_args = ['a', 'b', 'c', 'd', 'e'] 

    default_args = { 
     'c': 0, 
     'd': 1, 
     'e': 2 
    } 

    for index, value in enumerate(args): 
     kwargs[ordered_args[index]] = value 

    for arg in required_args: 
     if arg not in kwargs: 
      raise TypeError("Argument %s is required" % arg) 

    for arg in default_args: 
     if arg not in kwargs: 
      kwargs[arg] = default_args[arg] 

Mentre questo è un trucco accettabili, speravo davvero alla sottoclasse la originario TypeError sollevato, in modo che la funzione originale risultasse ancora "normale".

Problemi correlati