Apa itu mixin, dan mengapa mereka berguna?


955

Dalam " Pemrograman Python ", Mark Lutz menyebutkan "mixins". Saya dari latar belakang C / C ++ / C # dan saya belum pernah mendengar istilah sebelumnya. Apa itu mixin?

Membaca yang tersirat dari contoh ini (yang saya tautkan karena cukup panjang), saya anggap ini adalah kasus menggunakan multiple inheritance untuk memperluas kelas sebagai lawan dari subklasifikasi 'tepat'. Apakah ini benar?

Mengapa saya ingin melakukan itu daripada memasukkan fungsi baru ke dalam subkelas? Untuk itu, mengapa pendekatan mixin / multiple inheritance lebih baik daripada menggunakan komposisi?

Apa yang membedakan mixin dari multiple inheritance? Apakah ini hanya masalah semantik?

Jawaban:


710

Mixin adalah jenis khusus pewarisan berganda. Ada dua situasi utama di mana mixin digunakan:

  1. Anda ingin memberikan banyak fitur opsional untuk suatu kelas.
  2. Anda ingin menggunakan satu fitur tertentu di banyak kelas yang berbeda.

Untuk contoh nomor satu, pertimbangkan permintaan dan sistem respons werkzeug . Saya dapat membuat objek permintaan lama dengan mengatakan:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Jika saya ingin menambahkan dukungan tajuk terima, saya akan membuatnya

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Jika saya ingin membuat objek permintaan yang mendukung header terima, etag, otentikasi, dan dukungan agen pengguna, saya bisa melakukan ini:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

Perbedaannya halus, tetapi dalam contoh di atas, kelas mixin tidak dibuat untuk berdiri sendiri. Dalam pewarisan berganda yang lebih tradisional, AuthenticationMixin(misalnya) mungkin akan lebih mirip Authenticator. Artinya, kelas mungkin akan dirancang untuk berdiri sendiri.


123
Situasi ketiga adalah: Anda ingin memberikan banyak fitur (tidak-opsional) untuk kelas, tetapi Anda ingin fitur di kelas yang terpisah (dan dalam modul terpisah) sehingga setiap modul adalah tentang satu fitur (perilaku.) TKI, tidak untuk digunakan kembali, tetapi untuk kompartementalisasi.
bootchk

60
Mungkin bukan masalah dalam contoh ini, tetapi Anda umumnya ingin menempatkan kelas dasar utama sebagai elemen terakhir dalam tanda kurung sehingga membuat rantai pewarisan: Permintaan ==> Mixin ==> ... ==> BaseRequest. Lihat di sini: ianlewis.org/en/mixins-and-python
hillel

10
@hillel poin bagus, tetapi perlu diingat bahwa Python akan memanggil metode superclasses 'dari kiri ke kanan (ketika Anda perlu mengganti konstruktor, misalnya).
Eliseu Monar dos Santos

9
Ini terdengar seperti pola desain Penghias.
D-Jones

4
Situasi 4 adalah: sudah ada keluarga yang ada dari Parentkelas dan Child1, Child2, ChildNsubclass di dalam perpustakaan pihak ke-3, dan Anda ingin perilaku disesuaikan untuk seluruh keluarga. Idealnya Anda ingin menambahkan perilaku tersebut ke dalam Parent, dan berharap pengembang perpustakaan pihak ketiga akan menerima Permintaan Tarik Anda. Kalau tidak, Anda harus menerapkan sendiri class NewBehaviorMixin, dan kemudian menetapkan satu set penuh kelas pembungkus seperti class NewParent(NewBehaviorMixin, Parent): passdan class NewChildN(NewBehaviorMixin, ChildN): pass, dll. (PS: Apakah Anda tahu cara yang lebih baik?)
RayLuo

240

Pertama, Anda harus perhatikan bahwa mixin hanya ada dalam bahasa multi-inheritance. Anda tidak dapat melakukan mixin di Java atau C #.

Pada dasarnya, mixin adalah tipe dasar yang berdiri sendiri yang menyediakan fungsionalitas terbatas dan resonansi polimorfik untuk kelas anak. Jika Anda berpikir dalam bahasa C #, pikirkan sebuah antarmuka yang tidak harus Anda terapkan karena sudah diterapkan; Anda baru saja mewarisi dan manfaat dari fungsinya.

Mixin biasanya memiliki cakupan yang sempit dan tidak dimaksudkan untuk diperpanjang.

[sunting - alasan mengapa:]

Saya kira saya harus membahas mengapa, karena Anda bertanya. Manfaat besarnya adalah Anda tidak harus melakukannya sendiri berulang kali. Dalam C #, tempat terbesar di mana mixin bisa mendapat manfaat mungkin dari pola Pembuangan . Setiap kali Anda menerapkan IDisposable, Anda hampir selalu ingin mengikuti pola yang sama, tetapi Anda akhirnya menulis dan menulis ulang kode dasar yang sama dengan variasi kecil. Jika ada mixin Pembuangan yang bisa diperpanjang, Anda bisa menghemat banyak pengetikan.

[edit 2 - untuk menjawab pertanyaan Anda yang lain]

Apa yang membedakan mixin dari multiple inheritance? Apakah ini hanya masalah semantik?

Iya. Perbedaan antara mixin dan multiple inheritance standar hanyalah masalah semantik; kelas yang memiliki banyak pewarisan mungkin menggunakan mixin sebagai bagian dari pewarisan berganda itu.

Maksud dari mixin adalah untuk membuat tipe yang dapat "dicampur" ke tipe lain melalui pewarisan tanpa mempengaruhi tipe pewarisan sambil tetap menawarkan beberapa fungsi yang bermanfaat untuk tipe tersebut.

Sekali lagi, pikirkan sebuah antarmuka yang sudah diimplementasikan.

Saya pribadi tidak menggunakan mixin karena saya mengembangkannya terutama dalam bahasa yang tidak mendukung mereka, jadi saya mengalami waktu yang sangat sulit dengan contoh yang baik yang hanya akan memasok "ahah!" momen untukmu. Tapi saya akan coba lagi. Saya akan menggunakan contoh yang dibuat-buat - sebagian besar bahasa sudah menyediakan fitur dalam beberapa cara - tetapi itu akan, mudah-mudahan, menjelaskan bagaimana mixin seharusnya dibuat dan digunakan. Ini dia:

Misalkan Anda memiliki tipe yang Anda ingin dapat membuat serial ke dan dari XML. Anda ingin tipe menyediakan metode "ToXML" yang mengembalikan string yang berisi fragmen XML dengan nilai data dari tipe tersebut, dan "FromXML" yang memungkinkan tipe untuk merekonstruksi nilai datanya dari fragmen XML dalam string. Sekali lagi, ini adalah contoh yang dibuat-buat, jadi mungkin Anda menggunakan aliran file, atau kelas XML Writer dari pustaka runtime bahasa Anda ... apa pun. Intinya adalah Anda ingin membuat serial objek Anda menjadi XML dan mendapatkan objek baru kembali dari XML.

Poin penting lainnya dalam contoh ini adalah bahwa Anda ingin melakukan ini secara generik. Anda tidak ingin harus menerapkan metode "ToXML" dan "FromXML" untuk setiap jenis yang Anda ingin serialkan, Anda ingin beberapa cara umum untuk memastikan bahwa jenis Anda akan melakukan ini dan itu hanya berfungsi. Anda ingin menggunakan kembali kode.

Jika bahasa Anda mendukungnya, Anda dapat membuat mixin XmlSerializable untuk melakukan pekerjaan Anda untuk Anda. Jenis ini akan menerapkan metode ToXML dan FromXML. Itu akan, menggunakan beberapa mekanisme yang tidak penting untuk contoh, akan mampu mengumpulkan semua data yang diperlukan dari jenis apa pun yang dicampur dengan untuk membangun fragmen XML yang dikembalikan oleh ToXML dan itu akan sama-sama mampu mengembalikan data itu ketika FromXML adalah dipanggil.

Dan .. itu dia. Untuk menggunakannya, Anda akan memiliki tipe apa pun yang perlu diserialisasi ke XML dari XmlSerializable. Setiap kali Anda perlu membuat cerita bersambung atau membatalkan jenis itu, Anda cukup memanggil ToXML atau FromXML. Faktanya, karena XmlSerializable adalah tipe dan polimorfik yang lengkap, Anda dapat membuat serializer dokumen yang tidak tahu apa-apa tentang tipe asli Anda, hanya menerima, katakanlah, serangkaian tipe XmlSerializable.

Sekarang bayangkan menggunakan skenario ini untuk hal-hal lain, seperti membuat mixin yang memastikan bahwa setiap kelas yang mencampurnya dalam log setiap panggilan metode, atau mixin yang menyediakan transaksionalitas dengan tipe yang mencampurnya. Daftar ini dapat terus dan terus.

Jika Anda hanya menganggap mixin sebagai tipe basis kecil yang dirancang untuk menambahkan sejumlah kecil fungsionalitas ke tipe tanpa memengaruhi tipe itu, maka Anda berwarna emas.

Semoga. :)


25
Hei, apakah Anda suka ungkapan "resonansi polimorfik"? Membuatnya sendiri. Kupikir. Mungkin saya mendengarnya dalam fisika di suatu tempat ...
Randolpho

50
Saya sedikit tidak setuju dengan kalimat pertama Anda. Ruby adalah bahasa warisan tunggal dan mixin adalah cara untuk menambahkan metode ke kelas tertentu tanpa warisan dari kelas lain.
Keltia

23
@Keltia: Saya pikir mixin - menurut definisi - multiple-inheritance. Dalam kasus Ruby, mereka adalah monkeypatch (atau sesuatu yang lain) bukan mixin yang tepat. Orang-orang Ruby mungkin menyebutnya mixin, tapi itu hal yang berbeda.
S.Lott

10
Sebenarnya, mixin sejati tidak dapat menggunakan banyak pewarisan. Mixin mencakup metode, atribut, dll. Dari satu kelas di kelas lain tanpa mewarisinya. Hal ini cenderung memberikan manfaat penggunaan kembali kode dengan polimorfisme tetapi mengabaikan masalah menentukan asal usul (intan kematian, dll.) Bahasa pendukung Mixin juga cenderung memungkinkan masuknya sebagian dari kelas mixin (hal-hal mulai terdengar agak seperti aspek sekarang).
Trevor

8
Sebagai catatan, Java sekarang mendukung mixin dengan metode default.
shmosel

170

Jawaban ini bertujuan untuk menjelaskan mixin dengan contoh-contoh yaitu:

  • mandiri : singkat, tanpa perlu tahu perpustakaan apa pun untuk memahami contoh.

  • dalam Python , bukan dalam bahasa lain.

    Dapat dimengerti bahwa ada contoh-contoh dari bahasa lain seperti Ruby karena istilah ini jauh lebih umum dalam bahasa-bahasa itu, tetapi ini adalah utas Python .

Ini juga akan mempertimbangkan pertanyaan kontroversial:

Apakah pewarisan berganda diperlukan atau tidak untuk mengkarakterisasi mixin?

Definisi

Saya belum melihat kutipan dari sumber "otoritatif" yang dengan jelas mengatakan apa itu mixin dalam Python.

Saya telah melihat 2 kemungkinan definisi mixin (jika mereka dianggap berbeda dari konsep serupa lainnya seperti kelas dasar abstrak), dan orang-orang tidak sepenuhnya setuju yang mana yang benar.

Konsensus dapat bervariasi di antara berbagai bahasa.

Definisi 1: tidak ada banyak warisan

Mixin adalah kelas sedemikian sehingga beberapa metode kelas menggunakan metode yang tidak didefinisikan dalam kelas.

Oleh karena itu kelas tidak dimaksudkan untuk dipakai, tetapi berfungsi sebagai kelas dasar. Kalau tidak, instance akan memiliki metode yang tidak dapat dipanggil tanpa memunculkan pengecualian.

Kendala yang ditambahkan beberapa sumber adalah bahwa kelas mungkin tidak berisi data, hanya metode, tetapi saya tidak melihat mengapa ini perlu. Namun dalam praktiknya, banyak mixin berguna tidak memiliki data apa pun, dan kelas dasar tanpa data lebih mudah digunakan.

Contoh klasik adalah penerapan semua operator pembanding hanya dari <=dan ==:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

Contoh khusus ini bisa dicapai melalui functools.total_ordering()dekorator, tetapi permainan di sini adalah untuk menemukan kembali roda:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Definisi 2: pewarisan berganda

Mixin adalah pola desain di mana beberapa metode kelas dasar menggunakan metode yang tidak didefinisikan, dan metode itu dimaksudkan untuk diimplementasikan oleh kelas dasar lainnya , bukan oleh turunan seperti dalam Definisi 1.

Istilah class mixin mengacu pada kelas dasar yang dimaksudkan untuk digunakan dalam pola desain tersebut (TODO yang menggunakan metode, atau yang menerapkannya?)

Tidak mudah untuk memutuskan apakah kelas yang diberikan adalah mixin atau tidak: metode ini dapat diimplementasikan pada kelas turunan, dalam hal ini kita kembali ke Definisi 1. Anda harus mempertimbangkan niat penulis.

Pola ini menarik karena dimungkinkan untuk menggabungkan kembali fungsi dengan pilihan kelas dasar yang berbeda:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Kejadian Python resmi

Di dokumentasi resmi untuk koleksi.abc dokumentasi secara eksplisit menggunakan istilah Metode Mixin .

Ini menyatakan bahwa jika suatu kelas:

  • mengimplementasikan __next__
  • mewarisi dari satu kelas Iterator

maka kelas mendapat __iter__ metode mixin gratis.

Oleh karena itu setidaknya pada titik dokumentasi ini, mixin tidak memerlukan pewarisan berganda , dan koheren dengan Definisi 1.

Dokumentasi tentu saja dapat bertentangan pada titik yang berbeda, dan perpustakaan Python penting lainnya mungkin menggunakan definisi lain dalam dokumentasi mereka.

Halaman ini juga menggunakan istilah ini Set mixin, yang dengan jelas menunjukkan bahwa kelas menyukai Setdan Iteratordapat disebut kelas Mixin.

Dalam bahasa lain

  • Ruby: Jelas tidak memerlukan pewarisan berganda untuk mixin, seperti yang disebutkan dalam buku referensi utama seperti Pemrograman Ruby dan Bahasa pemrograman Ruby

  • C ++: Metode yang tidak diimplementasikan adalah metode virtual murni.

    Definisi 1 bertepatan dengan definisi kelas abstrak (kelas yang memiliki metode virtual murni). Kelas itu tidak bisa dipakai.

    Definisi 2 dimungkinkan dengan virtual inheritance: Multiple Inheritance dari dua kelas turunan


37

Saya menganggap mereka sebagai cara disiplin menggunakan multiple inheritance - karena pada akhirnya mixin hanyalah kelas python lain yang (mungkin) mengikuti konvensi tentang kelas yang disebut mixin.

Pemahaman saya tentang konvensi yang mengatur sesuatu yang Anda sebut Mixin adalah bahwa Mixin:

  • menambahkan metode tetapi bukan variabel instan (konstanta kelas OK)
  • hanya mewarisi dari object(dengan Python)

Dengan cara itu membatasi kompleksitas potensial dari banyak warisan, dan membuatnya cukup mudah untuk melacak aliran program Anda dengan membatasi di mana Anda harus melihat (dibandingkan dengan warisan penuh beberapa). Mereka mirip dengan modul ruby .

Jika saya ingin menambahkan variabel instan (dengan lebih banyak fleksibilitas daripada yang diizinkan oleh pewarisan tunggal) maka saya cenderung memilih komposisi.

Karena itu, saya telah melihat kelas yang disebut XYZMixin yang memiliki variabel instan.


30

Mixins adalah konsep dalam Pemrograman di mana kelas menyediakan fungsionalitas tetapi tidak dimaksudkan untuk digunakan untuk instantiasi. Tujuan utama Mixin adalah untuk menyediakan fungsionalitas yang berdiri sendiri dan akan lebih baik jika mixin itu sendiri tidak memiliki warisan dengan mixin lain dan juga menghindari keadaan. Dalam bahasa seperti Ruby, ada beberapa dukungan bahasa langsung tetapi untuk Python, tidak ada. Namun, Anda bisa menggunakan pewarisan multi-kelas untuk menjalankan fungsi yang disediakan dengan Python.

Saya menonton video ini http://www.youtube.com/watch?v=v_uKI2NOLEM untuk memahami dasar-dasar mixin. Sangat berguna bagi seorang pemula untuk memahami dasar-dasar mixin dan bagaimana mereka bekerja dan masalah yang mungkin Anda hadapi dalam mengimplementasikannya.

Wikipedia masih yang terbaik: http://en.wikipedia.org/wiki/Mixin


29

Apa yang membedakan mixin dari multiple inheritance? Apakah ini hanya masalah semantik?

Mixin adalah bentuk terbatas dari pewarisan berganda. Dalam beberapa bahasa, mekanisme untuk menambahkan mixin ke kelas sedikit berbeda (dalam hal sintaksis) dari pada inheritance.

Dalam konteks Python khususnya, mixin adalah kelas induk yang menyediakan fungsionalitas untuk subkelas tetapi tidak dimaksudkan untuk dipakai sendiri.

Apa yang mungkin menyebabkan Anda mengatakan, "itu hanyalah pewarisan berganda, bukan benar-benar mixin" adalah jika kelas yang mungkin bingung untuk mixin sebenarnya dapat dipakai dan digunakan - jadi memang itu adalah perbedaan semantik, dan sangat nyata.

Contoh Beberapa Warisan

Contoh ini, dari dokumentasi , adalah OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

Ini subkelas dari Counterdan OrderedDictdari collectionsmodul.

Keduanya Counterdan OrderedDictdimaksudkan untuk dipakai dan digunakan sendiri. Namun, dengan mensubklasifikasikan keduanya, kita dapat memiliki penghitung yang dipesan dan menggunakan kembali kode di setiap objek.

Ini adalah cara yang ampuh untuk menggunakan kembali kode, tetapi juga bisa menimbulkan masalah. Jika ternyata ada bug di salah satu objek, memperbaikinya tanpa perawatan bisa membuat bug di subkelas.

Contoh dari Mixin

Mixin biasanya dipromosikan sebagai cara untuk mendapatkan kembali penggunaan kode tanpa potensi masalah penggandaan yang dapat dimiliki beberapa pewarisan kooperatif, seperti OrderedCounter. Saat Anda menggunakan mixin, Anda menggunakan fungsionalitas yang tidak terlalu erat dengan data.

Berbeda dengan contoh di atas, mixin tidak dimaksudkan untuk digunakan sendiri. Ini menyediakan fungsionalitas baru atau berbeda.

Misalnya, perpustakaan standar memiliki beberapa mixin di socketserverperpustakaan .

Versi forking dan threading dari masing-masing jenis server dapat dibuat menggunakan kelas campuran ini. Misalnya, server ThreadingUDPS dibuat sebagai berikut:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

Kelas campuran datang pertama, karena menimpa metode yang didefinisikan dalam UDPServer. Mengatur berbagai atribut juga mengubah perilaku mekanisme server yang mendasarinya.

Dalam hal ini, metode mixin menimpa metode dalam UDPServerdefinisi objek untuk memungkinkan konkurensi.

Metode yang diganti tampaknya process_requestdan juga menyediakan metode lain process_request_thread,. Ini dia dari kode sumber :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

Sebuah Contoh Yang Dibuat

Ini adalah mixin yang sebagian besar untuk tujuan demonstrasi - sebagian besar objek akan berkembang melampaui manfaat dari repr ini:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

dan penggunaannya adalah:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

Dan penggunaan:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

11

Saya pikir ada beberapa penjelasan yang bagus di sini, tetapi saya ingin memberikan perspektif lain.

Di Scala, Anda dapat melakukan mixin seperti yang telah dijelaskan di sini, tetapi yang sangat menarik adalah bahwa mixin sebenarnya 'menyatu' bersama-sama untuk membuat kelas jenis baru untuk diwarisi. Intinya, Anda tidak mewarisi dari beberapa kelas / mixin, melainkan menghasilkan jenis kelas baru dengan semua properti mixin untuk diwarisi. Ini masuk akal karena Scala didasarkan pada JVM di mana multiple-inheritance saat ini tidak didukung (pada Java 8). Jenis kelas mixin, omong-omong, adalah tipe khusus yang disebut Trait in Scala.

Ini mengisyaratkan cara kelas didefinisikan: kelas NewClass memperluas FirstMixin dengan SecondMixin dengan ThirdMixin ...

Saya tidak yakin apakah juru bahasa CPython melakukan hal yang sama (komposisi kelas mixin) tapi saya tidak akan terkejut. Juga, berasal dari latar belakang C ++, saya tidak akan memanggil ABC atau 'antarmuka' yang setara dengan mixin - ini adalah konsep yang sama tetapi berbeda dalam penggunaan dan implementasi.


9

Saya akan menyarankan agar mix-in tidak dimasukkan dalam kode Python baru, jika Anda dapat menemukan cara lain di sekitarnya (seperti komposisi-bukan-dari-warisan, atau hanya metode patch-monyet ke dalam kelas Anda sendiri) yang tidak lebih upaya.

Di kelas gaya lama Anda bisa menggunakan mix-in sebagai cara meraih beberapa metode dari kelas lain. Tapi di dunia gaya baru segalanya, bahkan campuran, diwarisi dari object. Itu berarti bahwa setiap penggunaan pewarisan berganda secara alami menimbulkan masalah MRO .

Ada beberapa cara untuk membuat multi-inheritance MRO bekerja di Python, terutama fungsi super (), tetapi itu berarti Anda harus melakukan seluruh hierarki kelas menggunakan super (), dan jauh lebih sulit untuk memahami aliran kontrol.


3
Karena Versi 2.3 Python menggunakan "resolusi metode C3" yang dijelaskan dalam The Python 2.3 Method Resolution Order atau Method Resolution Order .
webwurst

11
Secara pribadi, saya akan meminum mixin daripada menambal monyet dalam banyak kasus; lebih mudah untuk memikirkan dan mengikuti kode.
tammers

5
Diturunkan. Sementara jawaban Anda mengekspresikan pendapat yang valid tentang gaya pengembangan, Anda tidak benar-benar menjawab pertanyaan yang sebenarnya.
Ryan B. Lynch

8

Mungkin beberapa contoh akan membantu.

Jika Anda membangun kelas dan Anda ingin itu bertindak seperti kamus, Anda dapat mendefinisikan semua __ __metode yang diperlukan. Tapi itu agak menyebalkan. Sebagai alternatif, Anda bisa mendefinisikan beberapa saja, dan mewarisi (selain warisan lainnya) dari UserDict.DictMixin(dipindahkan kecollections.DictMixin dalam py3k). Ini akan memiliki efek mendefinisikan semua api kamus secara otomatis.

Contoh kedua: toolkit GUI wxPython memungkinkan Anda membuat kontrol daftar dengan banyak kolom (seperti, misalnya, tampilan file di Windows Explorer). Secara default, daftar ini cukup mendasar. Anda dapat menambahkan fungsionalitas tambahan, seperti kemampuan untuk mengurutkan daftar dengan kolom tertentu dengan mengklik pada header kolom, dengan mewarisi dari ListCtrl dan menambahkan mixin yang sesuai.


8

Ini bukan contoh Python tetapi dalam bahasa pemrograman D istilah mixinini digunakan untuk merujuk ke konstruksi yang digunakan dengan cara yang sama; menambahkan setumpuk barang ke kelas.

Dalam D (yang omong-omong tidak melakukan MI) ini dilakukan dengan memasukkan templat (pikirkan makro yang sadar dan aman secara sintaksis dan Anda akan dekat) ke dalam cakupan. Ini memungkinkan satu baris kode di kelas, struct, fungsi, modul atau apa pun untuk diperluas ke sejumlah deklarasi.


2
Mixin adalah istilah umum, digunakan dalam D, Ruby, dll. Menurut Wikipedia, mereka berasal dari sistem sekolah tua, dan pertama kali didokumentasikan pada tahun 1983: en.wikipedia.org/wiki/…
Lee B

7

OP menyebutkan bahwa dia tidak pernah mendengar mixin dalam C ++, mungkin itu karena mereka disebut Curiously Recurring Template Pattern (CRTP) dalam C ++. @Ciro Santilli juga menyebutkan bahwa mixin diimplementasikan melalui kelas dasar abstrak dalam C ++. Sementara kelas dasar abstrak dapat digunakan untuk mengimplementasikan mixin, itu adalah kerja keras karena fungsi fungsi virtual pada saat run-time dapat dicapai dengan menggunakan template pada waktu kompilasi tanpa overhead dari pencarian tabel virtual pada saat run-time.

Pola CRTP dijelaskan secara rinci di sini

Saya telah mengonversi contoh python dalam jawaban @Ciro Santilli menjadi C ++ menggunakan kelas templat di bawah ini:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

EDIT: Menambahkan konstruktor yang dilindungi di ComparableMixin sehingga hanya bisa diwarisi dan tidak dipakai. Diperbarui contoh untuk menunjukkan bagaimana konstruktor yang dilindungi akan menyebabkan kesalahan kompilasi ketika objek ComparableMixin dibuat.


Mixin dan CRTP tidak persis sama di C ++.
ashrasmun

6

Mungkin contoh dari ruby ​​dapat membantu:

Anda dapat memasukkan mixin Comparabledan mendefinisikan satu fungsi "<=>(other)", mixin menyediakan semua fungsi tersebut:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

Ini dilakukan dengan memohon <=>(other) dan memberikan kembali hasil yang tepat.

"instance <=> other"mengembalikan 0 jika kedua objek sama, kurang dari 0 jika instancelebih besar dari otherdan lebih dari 0 jika otherlebih besar.


Berikut ini postingan yang menyediakan mixin serupa untuk Python. Meskipun saran tersebut didefinisikan __lt__sebagai basis dan bukan __cmp__, yang terakhir sebenarnya sudah usang dan tidak disarankan untuk digunakan. Bagi saya tampaknya sederhana untuk menggunakan mixin bukan cukup rumit dekorator (bagian dari functools ) - meskipun satu ini mungkin dapat bereaksi lebih dinamis di mana perbandingan disediakan ...
Tobias Kienzler

6

mixin memberikan cara untuk menambahkan fungsionalitas dalam kelas, yaitu Anda dapat berinteraksi dengan metode yang didefinisikan dalam modul dengan memasukkan modul di dalam kelas yang diinginkan. Meskipun ruby ​​tidak mendukung multiple inheritance tetapi memberikan mixin sebagai alternatif untuk mencapai itu.

di sini adalah contoh yang menjelaskan bagaimana pewarisan berganda dicapai menggunakan mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

4
Apa perbedaan antara ini dan multiple inheritance secara umum?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Perbedaannya adalah bahwa Anda tidak dapat membuat instance dari modul, tetapi jika tidak ada perbedaan antara kelas umum dan modul, mixin bukanlah hal yang eksplisit dan sulit dipahami di mana kelas umum dan di mana adalah mixin
ka8725

Jadi di Ruby mixin, hanya kelas yang tidak bisa dipakai tetapi harus digunakan untuk banyak pewarisan?
Trilarion

6

Saya hanya menggunakan python mixin untuk mengimplementasikan unit testing untuk python milters. Biasanya, seorang milter berbicara dengan MTA, membuat unit test sulit. Campuran tes mengesampingkan metode yang berbicara dengan MTA, dan menciptakan lingkungan simulasi yang didorong oleh kasus uji.

Jadi, Anda mengambil aplikasi milter yang tidak dimodifikasi, seperti spfmilter, dan mixin TestBase, seperti ini:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Kemudian, gunakan TestMilter dalam kasus uji untuk aplikasi yang lebih ringan:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup


4

Saya pikir tanggapan sebelumnya didefinisikan dengan sangat baik apa itu MixIns . Namun, untuk lebih memahami mereka, mungkin berguna untuk membandingkan MixIns dengan Kelas dan Antarmuka Abstrak dari perspektif kode / implementasi:

1. Kelas Abstrak

  • Kelas yang perlu mengandung satu atau lebih metode abstrak

  • Kelas Abstrak dapat berisi status (variabel instan) dan metode non-abstrak

2. Antarmuka

  • Antarmuka berisi metode abstrak hanya (tidak ada metode non-abstrak dan tidak ada negara internal)

3. MixIns

  • MixIns (seperti Antarmuka) tidak mengandung keadaan internal (variabel instan)
  • MixIns mengandung satu atau lebih metode non-abstrak (mereka dapat berisi metode non-abstrak tidak seperti antarmuka)

Dalam misalnya Python ini hanya konvensi, karena semua hal di atas didefinisikan sebagai classes. Namun, fitur umum dari kedua Kelas Abstrak, Antarmuka dan MixIns adalah bahwa mereka tidak boleh ada sendiri, yaitu tidak boleh dipakai.


3

Saya membaca bahwa Anda memiliki latar belakang ac #. Jadi titik awal yang baik mungkin implementasi mixin untuk .NET.

Anda mungkin ingin memeriksa proyek codeplex di http://remix.codeplex.com/

Tonton tautan Simposium lang.net untuk mendapatkan ikhtisar. Masih ada lagi yang muncul pada dokumentasi pada halaman codeplex.

salam Stefan

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.