Frage Wie implementiere ich Schnittstellen in Python?


public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

Wie implementiere ich Python-Äquivalent dieses C # -Codes?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

Ist das eine gute Idee? Bitte geben Sie Beispiele in Ihren Antworten.


75
2018-01-23 18:29


Ursprung


Antworten:


Wie von anderen hier erwähnt:

Schnittstellen sind in Python nicht notwendig. Dies liegt daran, dass Python eine korrekte mehrfache Vererbung und auch Ducktyping hat, was bedeutet, dass die Orte, an denen Sie sich befinden Muss Haben Sie Schnittstellen in Java, müssen Sie sie nicht in Python haben.

Das heißt, es gibt immer noch verschiedene Anwendungen für Schnittstellen. Einige von ihnen werden von Pythons Abstract Base Classes abgedeckt, die in Python 2.6 eingeführt wurden. Sie sind nützlich, wenn Sie Basisklassen erstellen möchten, die nicht instanziiert werden können, aber eine bestimmte Schnittstelle oder einen Teil einer Implementierung bereitstellen.

Eine andere Verwendung ist, wenn Sie irgendwie angeben möchten, dass ein Objekt eine bestimmte Schnittstelle implementiert, und Sie können ABC auch dafür verwenden, indem Sie von ihnen ableiten. Ein anderer Weg ist zope.interface, ein Modul, das Teil der Zope Component Architecture ist, ein wirklich cooles Komponentenframework. Hier untergliedern Sie sich nicht von den Schnittstellen, sondern markieren Klassen (oder sogar Instanzen) als Implementieren einer Schnittstelle. Dies kann auch zum Nachschlagen von Komponenten aus einer Komponentenregistrierung verwendet werden. Super cool!


71
2018-01-23 19:35



Die Verwendung des abc-Moduls für abstrakte Basisklassen scheint den Trick zu leisten.

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()

25
2018-04-17 13:11



Es gibt Implementierungen von Schnittstellen von Drittanbietern für Python (am beliebtesten ist Zopes, auch verwendet in Verdrehte), aber häufiger bevorzugen Python-Programmierer das reichhaltigere Konzept, das als "Abstract Base Class" (ABC) bekannt ist, das eine Schnittstelle mit der Möglichkeit kombiniert, einige Implementierungsaspekte auch dort zu haben. ABCs werden in Python 2.6 und höher besonders gut unterstützt, siehe die PEP, aber selbst in früheren Versionen von Python werden sie normalerweise als "der Weg zu gehen" angesehen - definieren Sie einfach eine Klasse, deren Methoden sich erhöhen NotImplementedError Damit Unterklassen auffallen, sollten sie diese Methoden besser überschreiben!


19
2018-01-23 18:33



So etwas (funktioniert möglicherweise nicht, da ich Python nicht habe):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

13
2018-01-23 18:36



Mein Verständnis ist, dass Schnittstellen in dynamischen Sprachen wie Python nicht notwendig sind. In Java (oder C ++ mit seiner abstrakten Basisklasse) sind Schnittstellen Mittel, um sicherzustellen, dass z.B. Sie übergeben den richtigen Parameter und können eine Reihe von Aufgaben ausführen.

Z.B. Wenn Sie beobachtbar und beobachtbar sind, ist Observable daran interessiert, Objekte zu abonnieren, die IObserver-Schnittstelle unterstützt, was wiederum hat notify Aktion. Dies wird zur Kompilierzeit überprüft.

In Python gibt es keine compile time und Methodenabfragen werden zur Laufzeit durchgeführt. Darüber hinaus kann man das Nachschlagen mit den magischen Methoden __getattr __ () oder __getattribute __ () überschreiben. Mit anderen Worten, Sie können als Beobachter jedes Objekt übergeben, das beim Zugriff abrufbar ist notify Attribut.

Dies führt mich zu der Schlussfolgerung, dass Schnittstellen in Python existieren - Es ist nur ihre Durchsetzung auf den Moment verschoben, in dem sie tatsächlich verwendet werden


6
2018-01-23 19:19



Die Implementierung von Schnittstellen mit abstrakten Basisklassen ist im modernen Python 3 viel einfacher und sie dienen einem Zweck als Schnittstellenvertrag für Plug-in-Erweiterungen.

Erstellen Sie die Schnittstelle / abstrakte Basisklasse:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

Erstellen Sie eine normale Unterklasse und überschreiben Sie alle abstrakten Methoden:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

Sie können optional eine gemeinsame Implementierung in den abstrakten Methoden wie in haben create_sale_invoice()mit ihm telefonieren super() explizit in der Unterklasse wie oben.

Instanziierung einer Unterklasse, die nicht alle abstrakten Methoden implementiert, schlägt fehl:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

Sie können auch abstrakte Eigenschaften, statische und Klassenmethoden verwenden, indem Sie entsprechende Anmerkungen mit kombinieren @abstractmethod.

Abstrakte Basisklassen eignen sich hervorragend zum Implementieren von pluginbasierten Systemen. Auf alle importierten Subklassen einer Klasse kann über zugegriffen werden __subclasses__(), also wenn Sie alle Klassen aus einem Plugin-Verzeichnis mit laden importlib.import_module() und wenn sie die Basisklasse ableiten, haben Sie direkten Zugriff auf sie über __subclasses__() und Sie können sicher sein, dass der Schnittstellenvertrag für alle von ihnen während der Instantiierung durchgesetzt wird.

Hier ist die Plugin-Lade-Implementierung für die AccountingSystem Beispiel oben:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

Dann können Sie auf das Accounting-System-Plugin-Objekt über die zugreifen AccountingSystem Klasse:

>>> accountingsystem = AccountingSystem.instance()

(Inspiriert von Dieser PyMOTW-3 Post.)


0
2017-07-10 20:06