Perbedaan antara kelas abstrak dan antarmuka dalam Python

Jawaban:

618

Apa yang akan Anda lihat kadang-kadang adalah sebagai berikut:

class Abstract1( object ):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

Karena Python tidak memiliki (dan tidak perlu) kontrak Antarmuka formal, perbedaan gaya Java antara abstraksi dan antarmuka tidak ada. Jika seseorang melalui upaya untuk mendefinisikan antarmuka formal, itu juga akan menjadi kelas abstrak. Satu-satunya perbedaan adalah dalam maksud yang dinyatakan dalam dokumen.

Dan perbedaan antara abstrak dan antarmuka adalah hal yang menarik ketika Anda mengetik bebek.

Java menggunakan antarmuka karena tidak memiliki banyak warisan.

Karena Python memiliki banyak pewarisan, Anda juga dapat melihat sesuatu seperti ini

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

Ini menggunakan semacam superclass abstrak dengan mixin untuk membuat subkelas beton yang terpisah.

S.Lott
sumber
5
S. Lott, maksud Anda karena bebek mengetik perbedaan antara has-a (interface) dan is-a (inheritance) tidak substansial?
Lorenzo
3
Perbedaan antara abstrak dan antarmuka adalah hal yang menarik ketika Anda mengetik bebek. Saya tidak tahu apa artinya "substansial". Ini "nyata" - memiliki substansi - dari perspektif desain. Tetapi dari perspektif bahasa mungkin tidak ada dukungan. Anda bisa mengadopsi konvensi untuk membedakan antara kelas abstrak dan definisi kelas antarmuka dengan Python.
S.Lott
26
@ L.DeLeo - apakah Anda yakin konsep has-a vs is-a benar? Saya biasanya melihat perbedaan sebagai memiliki-a = variabel anggota vs is-a = warisan (kelas induk atau antarmuka). Think sebanding atau Daftar di Jawa; itu adalah hubungan is-a, terlepas dari apakah itu antarmuka atau kelas abstrak.
dimo414
43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))adalah pesan kesalahan yang lebih informatif :)
naught101
9
@ Lorenzo a has-a relationship tidak ada hubungannya dengan pewarisan, pengetikan bebek, antarmuka, dan kelas abstrak (keempatnya merujuk pada hubungan is-a).
Karl Richter
197

Apa perbedaan antara kelas abstrak dan antarmuka dalam Python?

Antarmuka, untuk suatu objek, adalah seperangkat metode dan atribut pada objek itu.

Dengan Python, kita bisa menggunakan kelas dasar abstrak untuk mendefinisikan dan menjalankan antarmuka.

Menggunakan Kelas Basis Abstrak

Sebagai contoh, katakanlah kita ingin menggunakan salah satu kelas dasar abstrak dari collectionsmodul:

import collections
class MySet(collections.Set):
    pass

Jika kita mencoba menggunakannya, kita mendapatkan TypeErrorkarena kelas yang kita buat tidak mendukung perilaku set yang diharapkan:

>>> MySet()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__

Jadi kita dituntut untuk menerapkan di setidaknya __contains__ , __iter__, dan __len__. Mari kita gunakan contoh implementasi ini dari dokumentasi :

class ListBasedSet(collections.Set):
    """Alternate set implementation favoring space over speed
    and not requiring the set elements to be hashable. 
    """
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def __len__(self):
        return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2

Implementasi: Membuat Kelas Basis Abstrak

Kita dapat membuat Kelas Dasar Abstrak kita sendiri dengan mengatur metaclass ke abc.ABCMetadan menggunakan abc.abstractmethoddekorator pada metode yang relevan. Metaclass akan menambahkan fungsi-fungsi yang didekorasi ke __abstractmethods__atribut, mencegah instantiation hingga didefinisikan.

import abc

Sebagai contoh, "effable" didefinisikan sebagai sesuatu yang dapat diekspresikan dalam kata-kata. Katakanlah kita ingin mendefinisikan kelas dasar abstrak yang efektif, dengan Python 2:

class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Atau dengan Python 3, dengan sedikit perubahan dalam deklarasi metaclass:

class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Sekarang jika kita mencoba membuat objek yang efektif tanpa mengimplementasikan antarmuka:

class MyEffable(Effable): 
    pass

dan berusaha untuk instantiate:

>>> MyEffable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

Kami diberitahu bahwa kami belum menyelesaikan pekerjaan.

Sekarang jika kami mematuhi dengan menyediakan antarmuka yang diharapkan:

class MyEffable(Effable): 
    def __str__(self):
        return 'expressable!'

kita kemudian dapat menggunakan versi konkret dari kelas yang berasal dari abstrak:

>>> me = MyEffable()
>>> print(me)
expressable!

Ada hal-hal lain yang bisa kita lakukan dengan ini, seperti mendaftar subkelas virtual yang sudah mengimplementasikan antarmuka ini, tapi saya pikir itu di luar cakupan pertanyaan ini. Namun, metode lain yang diperlihatkan di sini harus mengadaptasi metode ini menggunakan abcmodul untuk melakukannya.

Kesimpulan

Kami telah menunjukkan bahwa pembuatan Kelas Basis Abstrak mendefinisikan antarmuka untuk objek khusus dalam Python.

Aaron Hall
sumber
101

Python> = 2.6 memiliki Kelas Basis Abstrak .

Abstrak Base Classes (disingkat ABC) melengkapi bebek-mengetik dengan menyediakan cara untuk mendefinisikan antarmuka ketika teknik lain seperti hasattr () akan kikuk. Python hadir dengan banyak ABC bawaan untuk struktur data (dalam modul koleksi), angka (dalam modul angka), dan stream (dalam modul io). Anda dapat membuat ABC Anda sendiri dengan modul abc.

Ada juga modul Zope Interface , yang digunakan oleh proyek di luar zope, seperti twisted. Saya tidak terlalu mengenalnya, tetapi ada halaman wiki di sini yang mungkin membantu.

Secara umum, Anda tidak memerlukan konsep kelas abstrak, atau antarmuka dalam python (diedit - lihat jawaban S.Lott untuk detailnya).

JimB
sumber
2
Apa yang Anda dapatkan dengan menggunakan ABC di Python?
CpILL
38

Python tidak benar-benar memiliki konsep apa pun.

Ini menggunakan mengetik bebek, yang menghilangkan kebutuhan untuk antarmuka (setidaknya untuk komputer :-))

Python <= 2.5: Kelas dasar jelas ada, tetapi tidak ada cara eksplisit untuk menandai metode sebagai 'virtual murni', sehingga kelas tidak benar-benar abstrak.

Python> = 2.6: Kelas dasar abstrak memang ada ( http://docs.python.org/library/abc.html ). Dan memungkinkan Anda untuk menentukan metode yang harus diimplementasikan dalam subkelas. Saya tidak terlalu suka dengan sintaks, tetapi fitur itu ada. Sebagian besar waktu mungkin lebih baik menggunakan itik mengetik dari sisi klien 'menggunakan'.

Douglas Leeder
sumber
3
Python 3.0 memang menambahkan kelas dasar abstrak nyata. Mereka digunakan dalam modul koleksi serta tempat-tempat lain. docs.python.org/3.0/library/abc.html
Lara Dougan
Referensi tentang mengapa mengetik bebek menghilangkan kebutuhan untuk antarmuka akan sangat membantu. Tampaknya tidak jelas bagi saya bahwa mengetik bebek, yang saya pahami sebagai kemampuan untuk "menyodok" metode atau atribut pada objek apa pun, akan berarti bahwa Anda tidak perlu menentukan perilaku yang diperlukan (dan meminta kompiler untuk mengingatkan Anda untuk mengimplementasikannya), itulah cara saya memahami Kelas Dasar Abstrak.
Reb.Cabin
Ini kurang mengetik bebek maka dukungan untuk multiple inheritence yang menghilangkan garis buatan antara antarmuka dan kelas abstrak yang misalnya Java telah ditarik.
masi
35

Dengan cara yang lebih mendasar untuk menjelaskan: Antarmuka adalah semacam panci muffin kosong. Ini adalah file kelas dengan sekumpulan definisi metode yang tidak memiliki kode.

Kelas abstrak adalah hal yang sama, tetapi tidak semua fungsi harus kosong. Beberapa dapat memiliki kode. Ini tidak sepenuhnya kosong.

Mengapa membedakan: Tidak ada banyak perbedaan praktis dalam Python, tetapi pada tingkat perencanaan untuk proyek besar, bisa lebih umum untuk berbicara tentang antarmuka, karena tidak ada kode. Terutama jika Anda bekerja dengan programmer Java yang terbiasa dengan istilah tersebut.

SilentSteel
sumber
+1 untuk perbedaan di mana ABC dapat memiliki implementasi sendiri - yang tampaknya cara yang sangat keren untuk mengakali diri sendiri
Titou
17

Secara umum, antarmuka hanya digunakan dalam bahasa yang menggunakan model kelas pewarisan tunggal. Dalam bahasa pewarisan tunggal ini, antarmuka biasanya digunakan jika kelas mana pun dapat menggunakan metode atau serangkaian metode tertentu. Juga dalam bahasa pewarisan-tunggal ini, kelas-kelas abstrak digunakan untuk menentukan variabel kelas selain dari tidak ada atau lebih metode, atau untuk mengeksploitasi model pewarisan tunggal untuk membatasi rentang kelas yang dapat menggunakan serangkaian metode.

Bahasa yang mendukung model multiple-inheritance cenderung hanya menggunakan kelas atau kelas dasar abstrak dan bukan antarmuka. Karena Python mendukung multiple inheritance, ia tidak menggunakan antarmuka dan Anda ingin menggunakan kelas dasar atau kelas dasar abstrak.

http://docs.python.org/library/abc.html

Lara Dougan
sumber
2

Kelas abstrak adalah kelas yang berisi satu atau lebih metode abstrak. Seiring dengan metode abstrak, kelas abstrak dapat memiliki metode statis, kelas dan contoh. Tetapi dalam hal antarmuka, itu hanya akan memiliki metode abstrak bukan yang lain. Oleh karena itu tidak diwajibkan untuk mewarisi kelas abstrak tetapi wajib untuk mewarisi antarmuka.

Akash Ukarande
sumber
1

Untuk kelengkapan, kita harus menyebutkan PEP3119 di mana ABC diperkenalkan dan dibandingkan dengan antarmuka, dan Talin asli komentar .

Kelas abstrak bukanlah antarmuka yang sempurna:

  • milik hierarki warisan
  • bisa berubah

Tetapi jika Anda mempertimbangkan untuk menuliskannya dengan cara Anda sendiri:

def some_function(self):
     raise NotImplementedError()

interface = type(
    'your_interface', (object,),
    {'extra_func': some_function,
     '__slots__': ['extra_func', ...]
     ...
     '__instancecheck__': your_instance_checker,
     '__subclasscheck__': your_subclass_checker
     ...
    }
)

ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...

Anda akan dengan cepat menyadari bahwa Anda sedang menciptakan roda untuk akhirnya mencapainya abc.ABCMeta

abc.ABCMeta diusulkan sebagai tambahan yang berguna dari fungsi antarmuka yang hilang, dan itu cukup adil dalam bahasa seperti python.

Tentu saja, itu dapat ditingkatkan lebih baik saat menulis versi 3, dan menambahkan sintaks baru dan konsep antarmuka yang tidak dapat diubah ...

Kesimpulan:

The abc.ABCMeta IS "pythonic" interface in python
Sławomir Lenart
sumber