2017-03-12 7 views
1

Ich habe eine spezielle Zustandsmaschine in Python implementiert, die Klassenmethoden als Zustandsdarstellung verwendet.Kann ich von der Klassenmethode in Python ableiten?

Besuchen Sie GitHub für den vollständigen Quellcode aus pyVHDLParser.


Wenn ich meine Parser FSM debuggen, erhalte ich die statenames gedruckt wie:

State: <bound method Package.stateParse of <class 'pyVHDLParser.DocumentModel.Sequential.Package.Package'>> 

Ich möchte besser Berichte bekommen, so möchte ich das Standardverhalten von __repr__ jeder Schranke zu überschreiben Methodenobjekt.

Ja, ich konnte eine Metaklasse schreiben oder einen zweiten Dekorateur anwenden, aber ich war in Frage mich:
Ist es möglich, von classmethod abzuleiten und haben nur einen Dekorateur genannt z.B. state?

Laut PyCharms builtins.py (eine Sammlung von Dummy-Code für Python-Builtins) ist classmethod ein klassenbasierter Decorator.

+0

Was ist der Grund, diese Klassenmethoden zu machen? Es macht 'EntityBlock' effektiv zu einem Singleton, aber selbst wenn Sie nur eine Instanz erstellen, scheint es komplexer zu sein. Insbesondere wenn Sie niemals Instanzen erstellen, wird '__init__' niemals aufgerufen. Wenn Sie Instanzen erstellen, teilen sie sich den Status, was weniger nützlich erscheint. – cco

+0

Dies ist ein minimales Beispiel. EntityBlock wird erstellt, andernfalls hätte es kein '__init__' ... Nein, sie teilen keinen Zustand, weil der" Zustand "die Methode und nicht die Klasse ist. Die Klasse ist eine Gruppe von Zuständen in einer hierarchischen Zustandsmaschine. Jedenfalls beantwortet es meine Frage nicht, oder? – Paebbels

+0

Rechts. Mein Kommentar ist nicht auf Ihre Frage gerichtet, ich reagierte nur auf etwas, das mir seltsam erschien. – cco

Antwort

1

Ja, Sie können Ihre eigene Klasse schreiben, die von stammt, wenn Sie möchten. Es ist ein bisschen kompliziert, obwohl. Sie müssen das Deskriptorprotokoll implementieren (Überschreiben der -Implementierung von), sodass es eine Instanz einer anderen benutzerdefinierten Klasse zurückgibt, die sich wie ein gebundenes Methodenobjekt verhält. Leider können Sie nicht von Pythons eingebautem gebundenem Methodentyp erben (ich bin nicht sicher, warum nicht).

Wahrscheinlich ist der beste Ansatz dann, eines der normalen Methodenobjekte in eine Instanz einer benutzerdefinierten Klasse zu verpacken. Ich bin mir nicht sicher, wie viel von der Methoden-API du replizieren musst, damit das ein wenig kompliziert wird. (Benötigen Sie Ihre Staaten, um miteinander vergleichbar zu sein? Müssen sie hashable? Pickable?)

Wie auch immer, hier ist eine nackte Knochen-Implementierung, die die minimale Menge notwendig, um eine Arbeitsmethode (plus die neue repr):

class MethodWrapper: 
    def __init__(self, name, method): 
     self.name = name if name is not None else repr(method) 
     self.method = method 

    def __call__(self, *args, **kwargs): 
     return self.method(*args, **kwargs) 

    def __repr__(self): 
     return self.name 

class State(classmethod): 
    def __init__(self, func): 
     self.name = None 
     super().__init__(func) 

    def __set_name__(self, owner, name): 
     self.name = "{}.{}".format(owner.__name__, name) 

    def __get__(self, owner, instance): 
     method = super().__get__(owner, instance) 
     return MethodWrapper(self.name, method) 

Und eine schnelle Demo davon in Aktion:

>>> class Foo: 
    @State 
    def foo(cls): 
     print(cls) 

>>> Foo.foo 
Foo.foo 
>>> Foo.foo() 
<class '__main__.Foo'> 
>>> f = Foo() 
>>> f.foo() 
<class '__main__.Foo'> 

Beachten sie, dass die __set_name__ Verfahren durch den State Descriptor verwendet nur von Python 3.6 genannt wird. Ohne dieses neue Feature wäre es für den Deskriptor viel schwieriger, seinen eigenen Namen zu lernen (Sie müssen möglicherweise eine Dekorator-Factory erstellen, die den Namen als Argument verwendet).

+0

Vergleich, Beizen und Hashing ist nicht erforderlich. Hat es Auswirkungen auf die Leistung? Ich meine, wenn ich die Methode/den Zustand anrufe? – Paebbels

+0

Der Aufruf der Methode über meine Methode wrapper, wie ich geschrieben habe, ist sicherlich langsamer als nur mit 'classmethod'. Es wird zweimal den Python-Funktionsaufruf-Overhead zu der Logik hinzufügen, die bereits einen Python-Funktionsaufruf (für den tatsächlichen Code der Methode) und zwei eingebaute Funktionsaufrufe (für die 'Klassenmethode .__ get__' und das Methodenobjekt'__call__') ausgeführt hat. Wie wichtig dieser zusätzliche Overhead ist, hängt davon ab, wie kompliziert der Rest des Codes ist. Wenn die meisten Ihrer State-Methoden in ihrem Körper nur "bestanden" haben und Ihr Programm die meiste Zeit damit verbringt, sie aufzurufen, kann der Leistungseinbruch groß sein. – Blckknght

Verwandte Themen