2016-06-23 4 views
5

Ich habe eine Klasse mit einem init, die viele Argumente braucht - einige erforderlich, andere nicht.Wie bekomme ich __init __(), um eine nützlichere Ausnahme anstelle von TypeError zu erzeugen, wenn die Argumente falsch sind?

Wenn eines der erforderlichen Argumente nicht angegeben wird, erhalten Sie möglicherweise einen TypeError mit einer nicht hilfreichen Nachricht wie 'erfordert mindestens 10 Argumente (14 gegeben)'.

Ich hätte gerne eine benutzerdefinierte TypeError-Unterklasse, die den Benutzer darüber informiert, welche Argumente fehlen.

+1

Sind diese Schlüsselwortargumente oder positional? – zwol

+0

@zwol kwargs (das kann positionell geliefert werden). –

+0

@zwol, das heißt ... alle Argumente sind benannte Argumente. Die Funktion sollte sich nicht darum kümmern, ob sie als Kwarts oder positionell geliefert werden, sie sollte wissen, welcher nach sich selbst fehlt, oder? –

Antwort

5

Wenn Sie Kwargs verwenden möchten, können Sie in init eine Liste der erforderlichen Argumente festlegen und dann überprüfen, ob alle erforderlichen Kwargs vorhanden sind. Schauen Sie sich das folgende Beispiel an.

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

Während ich diese Antwort bearbeiten musste, um ein wenig effizienter zu sein, und es dann umschreiben musste, um ein bisschen robuster zu sein, und unten vorausgesetzt, schien es passender zu sein, dieses zu "akzeptieren" ...Aber wenn Sie planen, dies tatsächlich zu implementieren, lesen Sie bitte meine Antwort unten. –

1

In Python 3 können Sie eine Funktion definieren, die "erforderliche Argumente nur für Schlüsselworte" benötigt. Dies ist am deutlichsten dokumentiert in PEP 3102. Die Fehlermeldung, die Sie erhalten, wenn Sie die erforderlichen Nur-Schlüssel-Argumente weglassen, enthält die Argumentnamen.

$ 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' 

Dies ist jedoch mit dem Code nicht kompatibel, die Argumente zu nennen X() mit Positions in der Lage sein erwartet, und die Fehlermeldung, die Sie erhalten, ist immer noch die, die Sie nicht mag:

>>> 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 

auch ist diese Funktion in einer beliebigen Version von Python nicht verfügbar 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 

Verbesserung der gegebenen Diagnostik Für Positionsargumente würde wahrscheinlich der Interpreter gehackt werden. Das Python-Entwicklungsteam könnte Patches zur Verfügung stellen. Ich würde erwägen, dies auf die Mailingliste python-ideas zu bringen.

+0

Ich werde tatsächlich dieser Liste beitreten und sie vorschlagen. Danke für die Empfehlung. –

0

Ich endete mit einer Variation von Tomas Gonzalez's answer, neu zu schreiben, so dass es die Mischung aus geordneten Argumenten, Schlüsselwort Argumenten und Standardwerten unterstützt, die es gab. Dies unterstützte unsere Anforderung, Code zu schreiben, der immer abwärtskompatibel zu Legacy-Code ist. Für "Greenfield" -Implementierungen ist Tomas 'Antwort vorzuziehen (und man würde immer nur mit Kwarts telefonieren). Für ältere Situationen wie meine (oder solche, bei dem eine Kombination von geordneten erforderlich und nicht erforderlichen Argumente erforderlich sein können?), Ist Folgendes zu beachten:

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] 

Während dies ein akzeptabler Hack ist, war ich wirklich die Hoffnung, Unterklasse Native TypeError ausgelöst, so dass die ursprüngliche Funktion immer noch "normal" aussehen würde.

Verwandte Themen