2017-08-25 3 views
1

Gibt es eine Möglichkeit zu verhindern, dass einige, aber nicht alle Argumente an eine Superklasse gesendet werden?Ist es möglich, selektiv zu entscheiden, welche Kwargs in Python an eine Superklasse übergeben werden sollen?

Ich habe eine Basisklasse, die Benutzereingabe überprüft:

class Base(object): 
    def __init__(self, **kwargs): 
     self.kwargs = kwargs 
     super(Base, self).__init__() 

    @staticmethod 
    def check_integrity(allowed, given): 
     """ 
     Verify user input. 
     :param allowed: list. Keys allowed in class 
     :param given: list. Keys given by user 
     :return: 
     """ 
     for key in given: 
      if key not in allowed: 
       raise Exception('{} not in {}'.format(key, allowed)) 

>>> base = Base() 
>>> print base.__dict__ 
output[0]: {'kwargs': {}} 

A erbt von Base und verwendet die Methode seiner Schlüsselwörter zu überprüfen

class A(Base): 
    def __init__(self, **kwargs): 
     super(A, self).__init__(**kwargs) 
     self.default_properties = {'a': 1, 
            'b': 2} 

     self.check_integrity(self.default_properties.keys(), kwargs.keys()) 

>>> a = A(a=4) 
>>> print a.__dict__ 
output[1]: {'default_properties': {'a': 1, 'b': 2}, 'kwargs': {'a': 4}} 

Ich sollte auch erwähnen, dass ich herausgezogen eine andere Methode, die ich in dieser Klasse für das Aktualisieren von Klasseneigenschaften verwendet habe, da es eine Komplikation ist, die für die Frage nicht relevant ist (daher wird a nicht auf 4 im obigen Beispiel aktualisiert)

Das Problem kommt, wenn von A und das Hinzufügen zusätzlicher kwargs zu der Unterklasse zu erben versuchen:

class B(A): 
    def __init__(self, **kwargs): 
     super(B, self).__init__(**kwargs) 

     self.default_properties = {'a': 2, 
            'c': 3, 
            'd': 4} 

     self.check_integrity(self.default_properties.keys(), kwargs.keys()) 



>>> b = B(d=5) 


Traceback (most recent call last): 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 112, in <module> 
    b = B(d=5) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 96, in __init__ 
    super(B, self).__init__(**kwargs) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 92, in __init__ 
    self.check_integrity(self.default_properties.keys(), kwargs.keys()) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 84, in check_integrity 
    raise Exception('{} not in {}'.format(key, allowed)) 
Exception: d not in ['a', 'b'] 

Hier d wird an die übergeordnete Klasse übergeben, auch wenn seine nur in der Unterklasse erforderlich. Die Argumente a und b werden jedoch in A verwendet und sollten von B an A übergeben werden.

+1

'del kwargs ['d']'? –

+0

Sie müssen 'kwargs' an die Superklasse übergeben, die nicht in' self.default_properties' enthalten sind. Ist das Ihre Erwartung @CiaranWelsh? – Rajez

+0

Ich möchte 'a' und' b' von 'B(). Default_properties()' an 'A' übergeben werden, aber nicht' d' von 'B(). Default_properties()'. – CiaranWelsh

Antwort

3

Gibt es eine Möglichkeit zu verhindern, dass einige, aber nicht alle Argumente an eine Superklasse gesendet werden?

Nun ja ganz einfach: nicht bestehen. Sie sollen wissen, welche Argumente Ihre Klasse nimmt und welche Argumente es übergeordnete Klasse (n) nehmen, übergeben, so nur, was die Superklasse erwartet:

class Base(object): 
    def __init__(self, arg1, arg2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

class Child(object): 
    def __init__(self, arg1, arg2, arg3): 
     super(Child, self).__init__(arg1, arg2) 
     self.arg3 = arg3 

Die oben ist klar, einfach lesbar und wartbar und garantierter zu arbeiten. Und wenn Sie Standardwerte wollen dann ist es auch kein Problem:

class Base(object): 
    def __init__(self, arg1=1, arg2=2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

class Child(object): 
    def __init__(self, arg1=1, arg2=2, arg3=3): 
     super(Child, self).__init__(arg1, arg2) 
     self.arg3 = arg3 

Nun, wenn Ihre Klasse responsability ist willkürlich Benutzereingaben gegen ein gegebenes „Schema“ (default_properties in Ihrem Snippet) zu bestätigen, gibt es in der Tat ein paar logische Fehler in Ihrem Code - hauptsächlich, dass Sie 1. Ihre Eingaben im Initialisierer validieren und 2. den Initialisierer des Elternteils VOR dem Überschreiben des default_properties Ihres Objekts aufrufen, wenn also der Superklasseninitialisierer aufgerufen wird, wird er nicht gegen das korrekte Schema validiert. Auch sind definieren Sie default_properties als Instanz Attribut in Ihrem initializer so, wenn Sie nur die Anweisungen tauschen zum ersten default_propertie definieren und erst dann die initializer der Eltern nennen dies eine default_properties

Eine einfache Lösung nur default_properties neu definieren würde, um eine Klasse Attribut:

class Base(object): 

    # empty by default 
    default_properties = {} 

    def __init__(self, **kwargs):     
     self.kwargs = kwargs 
     # object.__init__ is a noop so no need for a super call here 
     self.check_integrity()     

    def check_integrity(self): 
     """ 
     Verify user input. 
     """ 
     for key in self.kwargs: 
      if key not in self.default_properties: 
       raise ValueError('{} not allowed in {}'.format(key, self.default_properties)) 

und dann haben Sie nicht die initializer gar außer Kraft zu setzen:

class A(Base): 
    default_properties = {'a': 1, 
          'b': 2} 


class B(A): 
    default_properties = {'a': 1, 
          'b': 2, 
          'd': 3} 

und du bist don e - check_integrity verwendet die Klasse der aktuellen Instanz default_properties und Sie müssen sich nicht darum kümmern, "selektiv zu sein, welche Kwargs Sie an die Oberklasse weitergeben".

Nun ist dies immer noch viel zu simpel effektiv als Eingabevalidierung framewoek zu arbeiten, besonders, wenn Sie die Vererbung wollen ... Wenn B eine geeignete Unterklasse von A ist, sollte es in der Lage sein zu default_properties hinzuzufügen, ohne sie neu zu definieren vollständig (was eine offensichtliche DRY-Verletzung ist). Und die Validierung von Benutzereingaben ist normalerweise viel aufwendiger als das Überprüfen von Argumentnamen. Vielleicht möchten Sie untersuchen, wie andere Bibliotheken/Frameworks das Problem gelöst haben (Djangos Formen kommen hier in den Sinn).

+0

Hallo Bruno, wirklich schätzen Sie Ihre detaillierte Antwort - es scheint für mich unschätzbar wertvoll sein (als Autodidakt Python Programmierer). Ich hatte nur ein paar Fragen. Erstens, wenn ich die von Ihnen vorgeschlagenen Änderungen an der Klasse "base" vorstelle, wird durch Aufruf von 'self.check_integrity()' in der Klasse 'Base' ein' NameError' ausgelöst. Hier ist ein [Link] (http://www.tutorialspoint.com/execute_python_online.php?PID=0Bw_CjBb95KQMbElGSW9IYUdGSHM), um den Fehler zu demonstrieren. Zweitens, ist es notwendig, eine leere 'default_properties' zu definieren? Welchen Wert hat das? Vielen Dank. – CiaranWelsh

+0

@CiaranWesh gab es tatsächlich einen Fehler in meinem Code-Snippet, es ist jetzt behoben. Die leeren 'default_properties' sind nicht unbedingt erforderlich (und müssen in keiner Weise leer sein), aber ohne sie würde 'check_integrity()' für die untergeordnete Klasse ohne 'default_properties' brechen. Es macht auch die Absicht ein wenig klarer. –

Verwandte Themen