1

Ich habe eine Basisklasse, die viele direkte Unterklassen hat. Es gibt mehrere unabhängige Funktionen, die von mehreren Unterklassen gemeinsam genutzt werden. Dies ist ein guter Anwendungsfall für die kooperative Vererbung von Python. Die Features sollten jedoch das Verhalten von außen umbrechen, sodass sie früher in der Reihenfolge der Methodenauflösung sein müssen.Wie mische ich Verhalten mit Klassen-Dekoratoren in Python?

class WrappedSub(FeatureA, FeatureB, FeatureC, RealSub): 

    def __init__(self, *args, **kwargs): 
     FeatureA.__init__(foo=42) 
     FeatureB.__init__(bar=13) 
     FeatureC.__init__(foobar=546) 
     RealSub.__init__(*args, **kwargs) 

class RealSub(Base): 
    # Lots of code ... 

Es wäre schön, anstatt die Kind-Klassen zu dekorieren.

@Mixin(FeatureA, 42) 
@Mixin(FeatureB, 13) 
@Mixin(FeatureC, 546) 
class RealSub(Base): 
    # Lots of code ... 

Gerade, ich brauche einen @Mixin decorator wo der erste Block unterhalb äquivalent die zweiten sein.

@Mixin(Sub, *feature_args, **feature_kwargs) 
class RealSub: 
    # Lots of code ... 

class RealSub: 
    # Lots of code ... 
class WrappedSub(Feature, RealSub): 
    def __init__(self, *sub_args, **sub_kwargs): 
     Feature.__init__(self, *feature_args, **feature_kwargs) 
     RealSub.__init__(self, *sub_args, **sub_kwargs) 
RealSub = WrappedSub 

Wie ist das in Python 3 möglich?

Antwort

3

Sie können Pythons kooperatives Mehrfachvererbungssystem wahrscheinlich verwenden, um Ihre Mixinklassen zu schreiben, anstatt zu versuchen, sie als Klassen-Dekoratoren zu implementieren. So habe ich generell den Begriff "Mixin" verstanden, der in Python OOP verwendet werden soll.

class Base: 
    def method(self, param): 
     value = param + 18 
     return value 

class FeatureOne:    # this could inherit from Base 
    def method(self, param): 
     if param == 42: 
      return 13 
     else: 
      return super().method(param) # call next class in inheritance chain 

class Child(FeatureOne, Base): 
    def method(self, param): 
     value = super().method(param) 
     value *= 2 
     return value 

Dies ist nicht ganz das gleiche wie das, was Sie wollten, da es die FeatureOne Klasse ruft method Umsetzung zwischen den Base und Child Klassen Versionen, anstatt vor Child macht seine Sache. Sie könnten stattdessen eine neue Grandchild Klasse hinzufügen, die von den Feature s, die Sie zuerst interessieren, und Child zuletzt erbt, wenn Sie die Methoden nicht anpassen können, um in dieser Reihenfolge zu arbeiten (der Körper der Grandchild könnte leer sein).

Wenn Sie wirklich Dekoratoren verwenden möchten, um die Reihenfolge umzudrehen, denke ich, dass Sie es wahrscheinlich funktionieren lassen könnten, wenn der Dekorator eine "Enkel" -Klasse für Sie erstellt (obwohl er nichts über die normale Vererbungshierarchie weiß)). Hier ist ein grober Versuch eines mixin Dekorateur, die fast wie Sie arbeiten will:

def mixin(*mixin_classes, **mixin_kwargs): # decorator factory function 
    def decorator(cls): # decorator function 
     class wrapper(*mixin_classes, cls): 
      def __init__(self, *args, **kwargs): 
       wrapped_kwargs = mixin_kwargs.copy() # use the passed kwargs to update the 
       wrapped_kwargs.update(kwargs)  # mixin args, so caller can override 
       super().__init__(*args, **wrapped_kwargs) 
     # maybe modify wrapper's __name__, __qualname__, __doc__, etc. to match cls here? 
     return wrapper 
    return decorator 

Die mixin Klassen super().__init__(*args, **kwargs) aus ihrer eigenen __init__ Methode aufrufe sollen (wenn sie eine haben), aber sie können annehmen (und nicht weitergeben) keyword-nur Argumente ihrer eigenen, dass sie vom mixin Dekorateur übergeben werden sollen:

class FeatureOne: 
    def __init__(self, *args, foo, **kwargs): # note that foo is a keyword-only argument 
     self.foo = foo 
     super().__init__(*args, **kwargs) 

    def method(self, param): 
     if param == self.foo: 
      return 13 
     else: 
      return super().__method__(param) 

@mixin(FeatureOne, foo=42) 
class Child(Base): 
    def method(self, param): 
     return super().method(param) * 2 

der Dekorateur soll entweder arbeiten mit allen mixin Klassen zu einem Dekorateur Aufruf übergeben (zB @mixin(FeatureA, FeatureB, FeatureC, foo=42, bar=13, foobar=546)) oder mit mehreren verschachtelten Dekorateur Anrufe. Die MRO der letzten Klasse werden auf beide Arten gleich sein.

+0

Ich dachte über kooperative Vererbung nach und aktualisierte die Frage. Wie Sie bereits sagten, besteht das Problem darin, dass Funktionen das Verhalten der Unterklasse umbrechen. Vielleicht kann kooperative Vererbung mit einem '@ Mixin'-Dekorateur kombiniert werden? Eine andere Idee wäre, das Wrapping-Feature-Class-Objekt zu kopieren und die Wrapped-Sub-Klasse zu seinem '__bases__' hinzuzufügen. Denkst du, das könnte funktionieren? – danijar

Verwandte Themen