Bagaimana cara mengimplementasikan antarmuka dalam python?

182
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

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

    #endregion
}

Bagaimana cara saya menerapkan Python yang setara dengan kode C # ini?

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

Apakah ini ide yang bagus ?? Tolong beri contoh dalam jawaban Anda.

Pratik Deoghare
sumber
Apa tujuan penggunaan antarmuka dalam kasus Anda?
Bandi-T
23
Terus terang tidak ada tujuan sama sekali! Saya hanya ingin belajar apa yang harus dilakukan ketika Anda membutuhkan antarmuka dalam python?
Pratik Deoghare
18
raise NotImplementedErroradalah apa yang showseharusnya tubuh - tidak masuk akal untuk meningkatkan generik sepenuhnya Exceptionketika Python mendefinisikan built-in yang sangat spesifik! -)
Alex Martelli
2
Tidakkah seharusnya init menampilkan panggilan () di IInterface (atau menaikkan pengecualian itu sendiri) sehingga Anda tidak dapat membuat antarmuka abstrak?
Katastic Voyage
1
Saya dapat melihat beberapa kegunaan untuk ini ... katakanlah Anda memiliki objek yang ingin Anda pastikan memiliki tanda tangan tertentu. Dengan mengetik bebek Anda tidak dapat menjamin bahwa objek akan menjadi tanda tangan yang Anda harapkan. Terkadang mungkin berguna untuk memberlakukan beberapa pengetikan pada properti yang diketik secara dinamis.
Prime By Design

Jawaban:

151

Seperti yang disebutkan oleh yang lain di sini:

Antarmuka tidak diperlukan dalam Python. Ini karena Python memiliki banyak pewarisan yang tepat, dan juga ducktyping, yang berarti tempat di mana Anda harus memiliki antarmuka di Jawa, Anda tidak harus memilikinya di Python.

Yang mengatakan, masih ada beberapa kegunaan untuk antarmuka. Beberapa dari mereka dilindungi oleh Pythons Abstract Base Classes, diperkenalkan dengan Python 2.6. Mereka berguna, jika Anda ingin membuat kelas dasar yang tidak dapat dipakai, tetapi menyediakan antarmuka khusus atau bagian dari suatu implementasi.

Penggunaan lain adalah jika Anda entah bagaimana ingin menentukan bahwa suatu objek mengimplementasikan antarmuka tertentu, dan Anda dapat menggunakan ABC untuk itu juga dengan mensubklasifikasikan dari mereka. Cara lain adalah zope.interface, sebuah modul yang merupakan bagian dari Arsitektur Komponen Zope, kerangka kerja komponen yang benar-benar keren. Di sini Anda tidak membuat subkelas dari antarmuka, melainkan menandai kelas (atau bahkan instance) sebagai mengimplementasikan antarmuka. Ini juga dapat digunakan untuk mencari komponen dari komponen registri. Sangat keren!

Lennart Regebro
sumber
11
Bisakah Anda menjelaskan hal ini? 1. Bagaimana cara mengimplementasikan antarmuka seperti itu? 2. Bagaimana ini dapat digunakan untuk mencari komponen?
geoidesic
42
"Antarmuka tidak perlu dengan Python. Kecuali kalau ada."
Baptiste Candellier
8
antarmuka sebagian besar digunakan untuk memiliki hasil yang dapat diprediksi / menegakkan kebenaran anggota ketika melewati objek di sekitar. alangkah baiknya jika python mendukung ini sebagai opsi. itu juga akan memungkinkan alat pengembangan untuk memiliki kecerdasan yang lebih baik
Sonic Soul
1
Contoh akan sangat meningkatkan jawaban ini.
bob
5
"Ini karena Python memiliki pewarisan berganda yang tepat", siapa bilang antarmuka untuk pewarisan berganda?
adnanmuttaleb
76

Menggunakan modul abc untuk kelas dasar abstrak tampaknya melakukan trik.

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()
Peter Torpman
sumber
11
Yugghh, membutuhkan beberapa modul untuk apa yang harus menjadi bagian dari bahasa itu sendiri, atau tidak digunakan sama sekali, IMO.
Mike de Klerk
maksud Anda: if not server.version() == '1.0': raise ...? Saya tidak benar-benar mendapatkan garis ini. Sebuah penjelasan akan disambut.
Skandix
1
@MikedeKlerk saya sangat setuju. Sama seperti jawaban Python untuk mengetik; Saya tidak harus mengimpor modul untuk menyatakan saya ingin tipe menjadi tipe. Respons terhadap hal itu biasanya "baik Python diketik secara dinamis", tapi itu bukan alasan. Java + Groovy mengatasi masalah ini. Java untuk hal-hal statis, Groovy untuk hal-hal yang dinamis.
ubiquibacon
6
@MikedeKlerk, modul abc memang bawaan untuk python. Ini adalah pekerjaan yang lebih sedikit untuk mengatur beberapa pola-pola ini karena mereka sebagian besar tidak dibutuhkan dalam Python karena pola-pola alternatif yang dianggap 'lebih Pythonic'. Untuk sebagian besar pengembang, itu akan seperti yang Anda katakan, 'tidak digunakan sama sekali'. Namun, mengakui bahwa ada beberapa kasus yang sangat niche yang benar-benar membutuhkan kemampuan antarmuka ini, para pembuat Python menyediakan API yang mudah digunakan untuk memungkinkan hal itu.
David Culbreth
39

antarmuka mendukung Python 2.7 dan Python 3.4+.

Untuk menginstal antarmuka Anda harus

pip install python-interface

Kode Contoh:

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
sinar biru
sumber
7
Keuntungan utama dari perpustakaan ini, IMHO, adalah kegagalan awal yang diberikannya kepada Anda: Jika kelas Anda tidak mengimplementasikan antarmuka yang ditentukan dengan benar, Anda akan mendapatkan pengecualian segera setelah kelas dibaca - Anda bahkan tidak harus menggunakannya . Dengan kelas dasar abstrak Python sendiri, Anda mendapatkan pengecualian saat pertama kali instantiate kelas Anda, yang bisa jadi jauh di kemudian hari.
Hans
Tidak perlu, ABC menawarkan fungsionalitas yang sama dan terintegrasi.
Daniel
@DanielCasares apakah ABC menawarkan antarmuka yang sebenarnya atau maksud Anda bahwa kelas abstrak tanpa status atau implementasi adalah solusi yang ditawarkan ABC?
asaf92
36

Menerapkan antarmuka dengan kelas dasar abstrak jauh lebih sederhana di Python modern 3 dan mereka melayani tujuan sebagai kontrak antarmuka untuk ekstensi plug-in.

Buat antarmuka / kelas dasar abstrak:

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)

Buat subkelas normal dan timpa semua metode abstrak:

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)

Anda dapat secara opsional memiliki implementasi umum dalam metode abstrak seperti pada create_sale_invoice(), memanggilnya super()secara eksplisit dalam subkelas seperti di atas.

Instansiasi dari subkelas yang tidak menerapkan semua metode abstrak gagal:

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

Anda juga dapat memiliki properti abstrak, metode statis, dan kelas dengan menggabungkan anotasi yang sesuai dengannya @abstractmethod .

Kelas dasar abstrak sangat bagus untuk menerapkan sistem berbasis plugin. Semua subclass yang diimpor dari suatu kelas dapat diakses melalui __subclasses__(), jadi jika Anda memuat semua kelas dari direktori plugin dengan importlib.import_module()dan jika mereka mensubclass kelas dasar, Anda memiliki akses langsung ke mereka melalui__subclasses__() dan Anda dapat yakin bahwa kontrak antarmuka diberlakukan untuk semua mereka selama instantiasi.

Berikut implementasi plugin loading untuk AccountingSystemcontoh di atas:

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

Kemudian Anda dapat mengakses objek plugin sistem akuntansi melalui AccountingSystemkelas:

>>> accountingsystem = AccountingSystem.instance()

(Terinspirasi oleh pos PyMOTW-3 ini .)

Tuan
sumber
Pertanyaan: Untuk apa nama modul "ABC"?
Sebastian Nielsen
"ABC" adalah singkatan dari "Abstract Base Classes", lihat dokumen resmi
mrts
31

Ada implementasi pihak ketiga dari antarmuka untuk Python (paling populer adalah Zope's , juga digunakan dalam Twisted ), tetapi lebih umum coders Python lebih suka menggunakan konsep yang lebih kaya yang dikenal sebagai "Abstrak Base Class" (ABC), yang menggabungkan antarmuka dengan kemungkinan memiliki beberapa aspek implementasi di sana juga. ABC sangat didukung dengan Python 2.6 dan yang lebih baru, lihat PEP , tetapi bahkan dalam versi Python sebelumnya mereka biasanya dilihat sebagai "cara untuk pergi" - cukup mendefinisikan kelas beberapa metode yang dinaikkan NotImplementedErrorsehingga subclass akan menjadi dengan pemberitahuan bahwa mereka sebaiknya mengganti metode itu! -)

Alex Martelli
sumber
3
Ada implementasi antarmuka pihak ketiga untuk Python Apa artinya? Bisakah Anda jelaskan ABC?
Pratik Deoghare
2
Yah, aku akan mengambil masalah dengan ABC menjadi "lebih kaya". ;) Ada hal-hal zope.interface dapat melakukan itu ABC tidak bisa serta sebaliknya Tetapi sebaliknya Anda seperti biasa benar. +1
Lennart Regebro
1
@Alfred: Ini berarti modul seperti zope.interface tidak termasuk dalam pustaka standar, tetapi tersedia dari pypi.
Lennart Regebro
Saya masih kesulitan untuk mengonsep konsep ABC. Apakah mungkin bagi seseorang untuk menulis ulang twistedmatrix.com/documents/current/core/howto/components.html (IMHO, penjelasan yang sangat baik tentang konsep antarmuka) dalam hal ABC. Apakah ini masuk akal?
mcepl
21

Sesuatu seperti ini (mungkin tidak berfungsi karena saya tidak memiliki Python):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"
Bandit
sumber
2
Apa yang harus saya lakukan __init__(self)terhadap konstruktor?
Pratik Deoghare
1
Terserah kamu. Karena tidak ada pemeriksaan waktu kompilasi terhadap pembangunan objek dari kelas abstrak, Anda tidak akan mendapatkan perlindungan selama pengkodean / kompilasi. Akan ada konstruktor yang diwarisi, sehingga objek akan dibuat, cukup "kosong". Terserah Anda untuk memutuskan apakah Anda akan lebih baik dengan membiarkan ini terjadi dan menangkap kegagalan nanti, atau secara eksplisit menghentikan program saat itu juga dengan menerapkan konstruktor yang sama dengan melemparkan pengecualian.
Bandi-T
1
Itu sebabnya abc.ABCjauh lebih baik daripada menaikkan NotImplementedError- instantiation dari abc.ABCsubclass yang tidak mengimplementasikan semua metode abstrak gagal lebih awal, sehingga Anda terlindungi dari kesalahan. Lihat jawaban saya di bawah untuk melihat bagaimana kesalahannya.
mrts
8

Pemahaman saya adalah bahwa antarmuka tidak diperlukan dalam bahasa dinamis seperti Python. Dalam Java (atau C ++ dengan kelas dasar abstraknya) antarmuka adalah cara untuk memastikan bahwa misalnya Anda melewati parameter yang tepat, dapat melakukan serangkaian tugas.

Misalnya jika Anda memiliki pengamat dan dapat diamati, diamati tertarik untuk berlangganan objek yang mendukung antarmuka IObserver, yang pada gilirannya memiliki notifytindakan. Ini diperiksa pada waktu kompilasi.

Dalam Python, tidak ada yang namanya compile timedan metode pencarian dilakukan saat runtime. Selain itu, orang dapat mengganti pencarian dengan __getattr __ () atau __getattribute __ () metode ajaib. Dengan kata lain, Anda dapat mengirimkan, sebagai pengamat, objek apa pun yang dapat mengembalikan panggilan pada notifyatribut akses .

Ini membawa saya pada kesimpulan, bahwa antarmuka dalam Python memang ada - hanya saja penegakannya ditunda hingga saat mereka benar-benar digunakan

Tomasz Zieliński
sumber