2009-02-16 6 views
16

Also beginne ich ein Projekt mit Python, nachdem ich eine beträchtliche Zeit im statischen Land verbracht habe. Ich habe einige Projekte gesehen, die "Interfaces" machen, die wirklich nur Klassen ohne Implementierungen sind. Vorher würde ich über die Idee spotten und diesen Teil dieser Projekte ignorieren. Aber jetzt fange ich an, mich auf die Idee einzulassen."Schnittstellen" in Python: Ja oder Nein?

Nur so sind wir klar, eine Schnittstelle in Python würde wie folgt aussehen:

class ISomething(object): 
    def some_method(): 
     pass 
    def some_other_method(some_argument): 
     pass 

Beachten Sie, dass Sie nicht selbst zu einem der Verfahren vorbei sind, so erfordert, dass das Verfahren zu überschrieben werden heißen. Ich sehe dies als eine nette Form der Dokumentation und Vollständigkeitsprüfung.

Also was ist hier jeder Meinung über die Idee? Wurde ich von all den C# -Programmierungen, die ich gemacht habe, einer Gehirnwäsche unterzogen, oder ist das eine gute Idee?

+2

"erfordert, dass die Methode überschrieben werden muss": aber Sie können ISomething() aufrufen. Some_other_method() – Miles

+0

Scheint mir nicht sehr Pythonic. –

Antwort

10

Es gibt einige Fälle, in denen Schnittstellen sehr praktisch sein kann. Twisted macht fairly extensive use von Zope interfaces, und in einem Projekt arbeitete ich an Zope-Schnittstellen wirklich gut funktioniert. Enthougts Eigenschaften wurden vor kurzem hinzugefügt interfaces, aber ich habe keine Erfahrung mit ihnen.

Passen Sie auf Überbeanspruchung - Ente Typisierung und Protokolle sind ein grundlegender Aspekt von Python, verwenden Sie nur Schnittstellen, wenn sie unbedingt erforderlich sind.

5

Scheint irgendwie unnötig für mich - wenn ich solche Klassen schreibe, mache ich normalerweise die Basisklasse (ISomething) ohne Methoden und erwähne in der aktuellen Dokumentation, welche Methoden die Unterklassen überschreiben sollen.

3

Ich bin über etwas ähnliches mit meinem Python-Projekt zu tun, das einzige, was ich möchte hinzufügen, sind:

  • Extra lange, doc Strings in-Tiefe für jede Schnittstelle und alle abstrakten Methoden.
  • Ich würde in allen erforderlichen Argumenten hinzufügen, so gibt es eine endgültige Liste.
  • Eine Ausnahme statt 'pass' auslösen.
  • Präfix alle Methoden, so dass sie offensichtlich Teil der Schnittstelle sind - Schnittstelle Foo: def foo_method1()
+0

Nur hinzufügen - die spezifische Ausnahme zu erhöhen wäre NotImplementedError() – che

+1

Ich mag alle Ihre Vorschläge mit Ausnahme der 4.. Es ist unnötig ausführlich, IMO. Auch ein Link zu NotImplementedError docs: http://docs.python.org/library/exceptions.html#exceptions.NotImplementedError –

+1

+1: Raise NotImplementedError statt Pass. -1: Verwenden Sie niemals zusätzliche Namen wie foo_method. Noch nie. –

6

Ich glaube nicht, dass Schnittstellen der Codeumgebung etwas hinzufügen würden.

  • Methode Definition Erzwingen geschieht ohne sie. Wenn ein Objekt wie Foo erwartet wird und Methode bar() hat, und es nicht tut, wird es eine AttributeError werfen.
  • Wenn Sie einfach sicherstellen, dass eine Schnittstellenmethode definiert wird, ist dies nicht garantiert. Verhaltenseinheitstests müssen ohnehin vorhanden sein.
  • Es ist genauso effektiv, eine "lesen Sie diese oder sterben" -Seite zu schreiben, die beschreibt, welche Methoden Ihr Objekt muss kompatibel mit dem, was Sie stecken es als aufwändige Docstrings in einer Interface-Klasse, da Sie wahrscheinlich sind Ich werde sowieso Tests dafür haben. Einer dieser Tests kann Standard für alle kompatiblen Objekte sein, die den Aufruf und den Rückgabetyp jeder Basismethode überprüfen.
10

Der pythonische Weg ist, um Vergebung zu bitten, anstatt Erlaubnis zu erhalten.Schnittstellen sind alle zum Empfangen von Berechtigungen zum Ausführen einer Operation für ein Objekt. Python bevorzugt dies:

def quacker(duck): 
    try: 
     duck.quack(): 
    except AttributeError: 
     raise ThisAintADuckException 
4

Sie können eine Schnittstelle in einer dynamisch typisierten Sprache erstellen, aber es gibt keine Erzwingung der Schnittstelle zur Kompilierzeit. Der Compiler einer statisch getippten Sprache wird Sie warnen, wenn Sie vergessen, eine Methode einer Schnittstelle zu implementieren (oder zu vertippen!). Da Sie keine solche Hilfe in einer dynamisch typisierten Sprache erhalten, dient Ihre Schnittstellendeklaration nur als Dokumentation. (Was nicht unbedingt schlecht ist, ist nur, dass Ihre Interface-Deklaration keine Laufzeitvorteile gegenüber dem Schreiben von Kommentaren bietet.)

27

Ich bin mir nicht sicher, was der Sinn davon ist. Schnittstellen (in dieser Form sowieso) sollen weitgehend das Fehlen von Mehrfachvererbung umgehen. Aber Python hat MI, warum also nicht einfach eine abstrakte Klasse?

class Something(object): 
    def some_method(self): 
     raise NotImplementedError() 
    def some_other_method(self, some_argument): 
     raise NotImplementedError() 
+0

+1: Ich wollte das gleiche vorschlagen, aber du hast mich dazu geschlagen. Dies ist viel mehr Pythonic, weil es den richtigen Fehler gibt, wenn die Methode nicht implementiert wird, aber keine Methode erfordert, die implementiert wird, wenn sie nicht im Kontext der Unterklasse verwendet wird. –

+0

Beim Schreiben von Java schreibt man oft auch Interfaces plus abstrakte Klassen. Die Unterklasse erweitert eine abstrakte Klasse, die die Schnittstelle implementiert. Die Schnittstelle ist redundant, wird aber aufgrund der Konvention erwartet. Ente Typisierung entfernt die Idiotie, aber manchmal finde ich diese Art von Basisklasse sehr nützlich. –

11

In Python 2.6 und höher können Sie abstract base classes stattdessen verwenden. Diese sind nützlich, weil Sie dann testen können, ob etwas ein bestimmtes ABC mithilfe von "isinstance" implementiert. Wie in Python üblich, wird das Konzept nicht so streng durchgesetzt wie in einer strengen Sprache, aber es ist praktisch. Außerdem gibt es schöne idiomatische Möglichkeiten, abstrakte Methoden mit Dekoratoren zu deklarieren - siehe den obigen Link für Beispiele.

1

Ich persönlich benutze Schnittstellen sehr oft in Verbindung mit der Zope Component Architecture (ZCA). Der Vorteil ist nicht so sehr, Schnittstellen zu haben, sondern diese mit Adaptern und Utilities (Singletons) nutzen zu können.

z. Sie könnten einen Adapter erstellen, der eine Klasse übernehmen kann, die ISomething implementiert, aber sie an die Schnittstelle ISomethingElse anpasst. Im Grunde ist es ein Wrapper.

würde die ursprüngliche Klasse sein:

class MyClass(object): 
    implements(ISomething) 

    def do_something(self): 
     return "foo" 

Dann Schnittstelle vorstellen ISomethingElse ein Verfahren do_something_else hat(). Ein Adapter könnte wie folgt aussehen:

class SomethingElseAdapter(object): 
    implements(ISomethingElse) 
    adapts(ISomething) 

    def __init__(self, context): 
     self.context = context 

    def do_something_else(): 
     return self.context.do_something()+"bar" 

Sie würden dann diesen Adapter mit der Komponente Registry registrieren und Sie können es dann wie folgt verwenden:

>>> obj = MyClass() 
>>> print obj.do_something() 
"foo" 
>>> adapter = ISomethingElse(obj) 
>>> print adapter.do_something_else() 
"foobar" 

Was das gibt Ihnen die Möglichkeit, die verlängern ursprüngliche Klasse mit Funktionalität, die die Klasse nicht direkt bereitstellt. Sie können dies tun, ohne diese Klasse zu ändern (es könnte sich in einem anderen Produkt/einer anderen Bibliothek befinden), und Sie könnten diesen Adapter einfach durch eine andere Implementierung austauschen, ohne den Code zu ändern, der ihn verwendet. Es ist alles durch die Registrierung von Komponenten in der Initialisierungszeit getan.

Dies ist natürlich vor allem nützlich für Frameworks/Bibliotheken.

Ich denke, es braucht etwas Zeit, um sich daran zu gewöhnen, aber ich will wirklich nicht mehr ohne leben. Aber wie gesagt, es ist auch richtig, dass Sie genau überlegen müssen, wo es Sinn macht und wo nicht. Natürlich können Schnittstellen auch als Dokumentation der API nützlich sein. Es ist auch nützlich für Komponententests, in denen Sie testen können, ob Ihre Klasse diese Schnittstelle tatsächlich implementiert. Und last but not least schreibe ich gerne die Benutzeroberfläche und einige Doc-Tests, um die Idee zu bekommen, was ich eigentlich programmieren möchte.

Für weitere Informationen können Sie meine little introduction to it auschecken und es gibt eine quite extensive description of it's API.

1

Haben Sie sich PyProtocols? angesehen, es hat eine nette Schnittstellenimplementierung, die Sie betrachten sollten.