Come posso implementare le interfacce in Python?


182
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}

Come posso implementare l'equivalente Python di questo codice C #?

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!'

E 'questa una buona idea?? Si prega di fornire esempi nelle risposte.


Quale sarebbe lo scopo di utilizzare un'interfaccia nel tuo caso?
Bandi-T,

23
Francamente niente da fare! Voglio solo imparare cosa fare quando hai bisogno di interfacce in Python?
Pratik Deoghare,

18
raise NotImplementedErrorè quello showche dovrebbe essere il corpo - non ha senso alzare un valore completamente generico Exceptionquando Python ne definisce uno integrato perfettamente specifico! -)
Alex Martelli,

2
Non dovrebbe init chiamata show () in IInterface (o rilanciare l'eccezione stessa) e quindi non è possibile creare un'istanza un'interfaccia astratta?
Katastic Voyage,

1
Riesco a vedere un certo uso per questo ... diciamo che hai un oggetto che vuoi assicurare abbia una firma specifica. Con la digitazione anatra non puoi garantire che l'oggetto sarà della firma che ti aspetti. A volte potrebbe essere utile imporre una digitazione su proprietà tipizzate dinamicamente.
Prime By Design,

Risposte:


151

Come menzionato da altri qui:

Le interfacce non sono necessarie in Python. Questo perché Python ha un'eredità multipla corretta, e anche ducktyping, il che significa che i luoghi in cui è necessario disporre di interfacce in Java, non è necessario averli in Python.

Detto questo, ci sono ancora diversi usi per le interfacce. Alcuni di essi sono coperti da Pythons Abstract Base Classes, introdotto in Python 2.6. Sono utili se si desidera creare classi di base che non possono essere istanziate, ma fornire un'interfaccia specifica o parte di un'implementazione.

Un altro utilizzo è se in qualche modo si desidera specificare che un oggetto implementa un'interfaccia specifica e si può usare anche ABC per quello subclassando da loro. Un altro modo è zope.interface, un modulo che fa parte di Zope Component Architecture, un framework di componenti davvero straordinario. Qui non si esegue la sottoclasse dalle interfacce, ma si contrassegnano invece le classi (o persino le istanze) come implementazione di un'interfaccia. Questo può anche essere usato per cercare componenti da un registro componenti. Supercool!


11
Potresti approfondire questo? 1. Come si implementa una tale interfaccia? 2. Come può essere utilizzato per cercare componenti?
geoidesic,

43
"Le interfacce non sono necessarie in Python. Tranne quando lo sono."
Baptiste Candellier,

8
le interfacce sono usate principalmente per avere un risultato prevedibile / imporre la correttezza dei membri quando si passano oggetti. sarebbe bello se python lo supportasse come opzione. consentirebbe inoltre agli strumenti di sviluppo di avere un migliore intellisense
Sonic Soul

1
Un esempio migliorerebbe notevolmente questa risposta.
bob

5
"Questo perché Python ha un'eredità multipla corretta", chi ha detto che le interfacce sono per l'ereditarietà multipla?
adnanmuttaleb,

76

L'uso del modulo abc per le classi di base astratte sembra fare il trucco.

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()

11
Yugghh, che ha bisogno di un modulo per quello che dovrebbe essere parte del linguaggio stesso, o non utilizzato affatto, IMO.
Mike de Klerk

vuoi dire if not server.version() == '1.0': raise ...:? Non capisco davvero questa linea. Una spiegazione sarebbe benvenuta.
Skandix,

1
@MikedeKlerk Non potrei essere più d'accordo. Proprio come la risposta di Python alla digitazione; Non dovrei importare un modulo per dichiarare che voglio che un tipo sia un tipo. La risposta a questo è in genere "bene Python è digitato in modo dinamico", ma non è una scusa. Java + Groovy risolvono questo problema. Java per roba statica, Groovy per roba dinamica.
ubiquibacon,

6
@MikedeKlerk, il modulo abc è infatti integrato in Python. È necessario un po 'più di lavoro per impostare alcuni di questi schemi perché sono in gran parte non necessari in Python a causa di schemi alternativi che sono considerati "più Pythonic". Per la stragrande maggioranza degli sviluppatori, sarebbe come hai detto, "non utilizzato affatto". Tuttavia, riconoscendo che ci sono alcuni casi molto di nicchia che richiedono veramente queste funzionalità di interfacciamento, i creatori di Python hanno fornito un'API di facile utilizzo per renderlo possibile.
David Culbreth,

39

l'interfaccia supporta Python 2.7 e Python 3.4+.

Per installare l' interfaccia devi

pip install python-interface

Codice di esempio:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y

7
Un grande vantaggio di questa libreria, IMHO, è il primo errore che ti dà: se la tua classe non implementa correttamente un'interfaccia specificata, ricevi un'eccezione non appena la classe viene letta, non devi nemmeno usarla . Con la propria classe base astratta di Python, si ottiene l'eccezione quando si crea un'istanza per la prima volta, che potrebbe essere molto più tardi.
Hans,

Non è necessario, ABC offre una funzionalità integrata simile.
Daniel

@DanielCasares ABC offre un'interfaccia reale o vuoi dire che le classi astratte senza stato o implementazioni sono la soluzione che offre ABC?
asaf92,

36

L'implementazione di interfacce con classi di base astratte è molto più semplice nel moderno Python 3 e hanno uno scopo come contratto di interfaccia per estensioni di plug-in.

Creare l'interfaccia / la classe base astratta:

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)

Creare una sottoclasse normale e sovrascrivere tutti i metodi astratti:

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)

Opzionalmente puoi avere un'implementazione comune nei metodi astratti come in create_sale_invoice(), chiamandolo super()esplicitamente nella sottoclasse come sopra.

L'istanza di una sottoclasse che non implementa tutti i metodi astratti ha esito negativo:

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

Puoi anche avere proprietà astratte, metodi statici e di classe combinando le corrispondenti annotazioni con @abstractmethod.

Le classi di base astratte sono ottime per l'implementazione di sistemi basati su plugin. Tutte le sottoclassi importate di una classe sono accessibili tramite __subclasses__(), quindi se si caricano tutte le classi da una directory di plug-in importlib.import_module()e se si esegue la sottoclasse della classe di base, si ha accesso diretto ad esse tramite __subclasses__()e si può essere certi che il contratto di interfaccia sia applicato per tutti loro durante l'istanza.

Ecco l'implementazione di caricamento plug-in per l' AccountingSystemesempio sopra:

...
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

Quindi è possibile accedere all'oggetto plugin del sistema di contabilità attraverso la AccountingSystemclasse:

>>> accountingsystem = AccountingSystem.instance()

(Ispirato da questo post di PyMOTW-3 .)


Domanda: Cosa significa il nome del modulo "ABC"?
Sebastian Nielsen,

"ABC" sta per "Classi di base astratte", vedi documenti ufficiali
sig.

31

Esistono implementazioni di terze parti di interfacce per Python (la più popolare è Zope , utilizzata anche in Twisted ), ma più comunemente i programmatori Python preferiscono utilizzare il concetto più ricco noto come "Abstract Base Class" (ABC), che combina un'interfaccia con la possibilità di avere anche alcuni aspetti di implementazione. Gli ABC sono particolarmente ben supportati in Python 2.6 e versioni successive, vedi PEP , ma anche nelle versioni precedenti di Python sono normalmente visti come "la strada da percorrere" - basta definire una classe che alcuni dei loro metodi sollevano in NotImplementedErrormodo che le sottoclassi siano notando che faranno meglio a ignorare quei metodi! -)


3
Esistono implementazioni di interfacce di terze parti per Python Cosa significa? Potresti spiegare ABC?
Pratik Deoghare,

2
Bene, mi metterò in dubbio che la ABC sia "più ricca". ;) Ci sono cose che zope.interface può fare che ABC non può fare al contrario. Ma per il resto hai ragione come al solito. +1
Lennart Regebro,

1
@Alfred: significa che moduli come zope.interface non sono inclusi nella libreria standard, ma sono disponibili su pypi.
Lennart Regebro,

Ho ancora difficoltà a parlare del concetto di ABC. Sarebbe possibile per qualcuno riscrivere twistedmatrix.com/documents/current/core/howto/components.html (IMHO, un'eccellente spiegazione del concetto di interfacce) in termini di ABC. Ha un senso?
mcepl,

21

Qualcosa del genere (potrebbe non funzionare perché non ho Python in giro):

class IInterface:
    def show(self): raise NotImplementedError

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

2
Cosa devo fare per __init__(self)il costruttore?
Pratik Deoghare,

1
Sta a te. Poiché non esiste un controllo in fase di compilazione contro la costruzione di un oggetto da una classe astratta, non si otterrebbe alcuna protezione durante la codifica / compilazione. Ci sarà un costruttore ereditato, quindi l'oggetto verrebbe creato, semplicemente "vuoto". Spetta a te decidere se faresti meglio a consentire che ciò accada e rilevare gli errori in un secondo momento, o interrompere esplicitamente il programma in quel momento e lì implementando un costruttore simile che genera un'eccezione.
Bandi-T,

1
Ecco perché abc.ABCè molto meglio che rilanciare NotImplementedError: l'istanza di una abc.ABCsottoclasse che non implementa tutti i metodi astratti fallisce presto, quindi sei protetto dagli errori. Vedi la mia risposta qui sotto per come appare l'errore.
sig.

8

La mia comprensione è che le interfacce non sono così necessarie in linguaggi dinamici come Python. In Java (o C ++ con la sua classe base astratta) le interfacce sono mezzi per garantire che, ad esempio, si passi il parametro giusto, in grado di eseguire una serie di attività.

Ad esempio, se hai osservatore e osservabile, osservabile è interessato a sottoscrivere oggetti che supportano l'interfaccia IObserver, che a sua volta ha notifyazione. Questo viene verificato al momento della compilazione.

In Python non è possibile compile timeeseguire ricerche di metodo durante l'esecuzione. Inoltre, è possibile ignorare la ricerca con i metodi magici __getattr __ () o __getattribute __ (). In altre parole, è possibile passare, come osservatore, qualsiasi oggetto che può restituire richiamabile quando si accede notifyall'attributo.

Questo mi porta alla conclusione che esistono interfacce in Python - è solo la loro applicazione è rinviata al momento in cui vengono effettivamente utilizzate

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.