Mixin nedir ve neden faydalıdırlar?


954

" Programlama Python " da, Mark Lutz "mixins" den bahseder. Ben bir C / C ++ / C # arka plandayım ve daha önce bu terimi duymadım. Mixin nedir?

Bu örneğin satırları arasında (oldukça uzun olduğu için bağlandığım) okuduğumda, bir sınıfı 'uygun' alt sınıflamanın aksine genişletmek için çoklu kalıtım kullanmanın bir durum olduğunu düşünüyorum. Bu doğru mu?

Yeni işlevselliği bir alt sınıfa koymak yerine neden bunu yapmak isteyeyim ki? Bu nedenle, bir mixin / çoklu kalıtım yaklaşımı neden kompozisyon kullanmaktan daha iyi olsun?

Bir mixini çoklu kalıtımdan ayıran nedir? Bu sadece bir anlambilim midir?

Yanıtlar:


710

Bir mixin özel bir çeşit çoklu kalıtımdır. Karışımların kullanıldığı iki ana durum vardır:

  1. Bir sınıf için birçok isteğe bağlı özellik sağlamak istiyorsunuz.
  2. Belirli bir özelliği birçok farklı sınıfta kullanmak istiyorsunuz.

Bir numaralı örnek için werkzeug'un istek ve yanıt sistemini düşünün . Basit bir eski istek nesnesi söyleyerek yapabilirim:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Kabul et başlık desteği eklemek istersem,

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Kabul üstbilgileri, etags, kimlik doğrulama ve kullanıcı aracısı desteğini destekleyen bir istek nesnesi yapmak istedim, bunu yapabilirim:

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

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

Fark incedir, ancak yukarıdaki örneklerde, mixin sınıfları kendi başlarına durmak için yapılmamıştır. Daha geleneksel çoklu mirasta, AuthenticationMixin(örneğin) muhtemelen daha çok benzer bir şey olacaktır Authenticator. Yani, sınıf muhtemelen kendi başına duracak şekilde tasarlanacaktır.


123
Üçüncü bir durum: bir sınıf için çok sayıda (isteğe bağlı olmayan) özellik sağlamak istiyorsunuz, ancak ayrı sınıflardaki (ve ayrı modüllerdeki) özellikleri istiyorsunuz, böylece her modül yaklaşık bir özellik (davranış.) IOW, değil yeniden kullanım için, ancak bölümlendirme için.
bootchk

60
Muhtemelen bu örnekte bir sorun değil, ancak miras zincirini oluşturmak için genellikle ana temel sınıfı parantez içindeki son öğe olarak koymak istiyorsunuz: Request ==> Mixin ==> ... ==> BaseRequest. Buraya bakın: ianlewis.org/tr/mixins-and-python
hillel

10
@hillel iyi bir nokta, ancak Python'un üst sınıf yöntemlerini soldan sağa çağıracağını unutmayın (örneğin kurucuyu geçersiz kılmanız gerektiğinde).
Eliseu Monar dos Santos

9
Bu Dekoratör tasarım deseni gibi görünüyor.
D-Jones

4
Bir 4 durumdur: Orada mevcut bir aile zaten Parentsınıf ve Child1, Child2, ChildNbir 3. parti kitaplığı içinde alt sınıfları ve tüm aile için özelleştirilmiş bir davranış istiyorum. İdeal olarak Parent, bu tür davranışları eklemek istersiniz ve 3. taraf kütüphane geliştiricisinin Çekme Talebinizi alacağını umuyoruz. Aksi takdirde kendi uygulamanızı uygulamanız class NewBehaviorMixinve ardından class NewParent(NewBehaviorMixin, Parent): passve class NewChildN(NewBehaviorMixin, ChildN): passvb. Gibi bir dizi sarıcı sınıf tanımlamanız gerekir . (PS: Daha iyi bir yol biliyor musunuz?)
RayLuo

240

İlk olarak, karışımların sadece çoklu kalıtım dillerinde bulunduğunu unutmayın. Java veya C # ile mixin yapamazsınız.

Temel olarak, bir mixin, bir çocuk sınıfı için sınırlı işlevsellik ve polimorfik rezonans sağlayan bağımsız bir baz türüdür. C # düşünürseniz, zaten uygulanmak zorunda olmadığınız bir arabirimi düşünün; sadece miras alırsınız ve işlevselliğinden faydalanırsınız.

Karışımların kapsamı tipik olarak dardır ve uzatılması amaçlanmamıştır.

[değiştir - neden olarak:]

Sanırım nedenini ele almalıyım, çünkü sordunuz En büyük yararı, bunu kendiniz tekrar tekrar yapmak zorunda kalmamanızdır. C # 'da, bir karışımın faydalanabileceği en büyük yer Bertaraf paterninden olabilir . IDisposable'ı her uyguladığınızda, neredeyse her zaman aynı kalıbı takip etmek istersiniz, ancak aynı temel kodu küçük varyasyonlarla yazar ve yeniden yazarsınız. Uzatılabilir bir Bertaraf karışımı varsa, kendinizi fazladan yazarak kurtarabilirsiniz.

[değiştir 2 - diğer sorularınızı cevaplamak için]

Bir mixini çoklu kalıtımdan ayıran nedir? Bu sadece bir anlambilim midir?

Evet. Bir mixin ve standart çoklu kalıtım arasındaki fark sadece bir anlambilim meselesidir; çoklu kalıtıma sahip bir sınıf, bu çoklu kalıtımın bir parçası olarak bir mixin kullanabilir.

Bir karışımın amacı, kalıtım türünü etkilemeden kalıtım yoluyla başka herhangi bir türe "karıştırılabilen" bir tür yaratmak ve aynı zamanda bu tür için bazı yararlı işlevler sunmaktır.

Yine, zaten uygulanmış olan bir arayüzü düşünün.

Öncelikle onları desteklemeyen bir dilde geliştirdiğim için kişisel olarak mixins kullanmıyorum, bu yüzden sadece "ahah!" Sağlayacak iyi bir örnekle gelmekte gerçekten zorlanıyorum. Senin için an. Ama tekrar deneyeceğim. Bir örnek kullanacağım - çoğu dil zaten bir şekilde bu özelliği sağlıyor - ama umarım, karışımların nasıl yaratılması ve kullanılması gerektiğini açıklar. İşte gidiyor:

XML'e ve XML'den serileştirmek istediğiniz bir türünüz olduğunu varsayalım. Türün, türün veri değerlerine sahip bir XML parçası içeren bir dize döndüren bir "ToXML" yöntemi ve türün veri değerlerini bir dizedeki XML parçasından yeniden oluşturmasına izin veren bir "FromXML" sağlamasını istiyorsunuz. Yine, bu anlaşılmaz bir örnektir, bu yüzden dilinizin çalışma zamanı kitaplığından bir dosya akışı veya bir XML Writer sınıfı kullanırsınız ... her neyse. Mesele, nesnenizi XML'e serileştirmek ve XML'den yeni bir nesne almak istediğinizdir.

Bu örnekteki diğer önemli nokta, bunu genel bir şekilde yapmak istemenizdir. Serileştirmek istediğiniz her tür için bir "ToXML" ve "FromXML" yöntemi uygulamak zorunda kalmazsınız, türünüzün bunu yapmasını ve sadece çalışmasını sağlamak için bazı genel yöntemler istersiniz. Kodun yeniden kullanılmasını istiyorsunuz.

Diliniz destekliyorsa, işinizi sizin yerinize yapmak için XmlSerializable karışımı oluşturabilirsiniz. Bu tür, ToXML ve FromXML yöntemlerini uygular. Örnek için önemli olmayan bir mekanizma kullanarak, ToXML tarafından döndürülen XML parçasını oluşturmak için karıştırıldığı herhangi bir türden gerekli tüm verileri toplayabilir ve FromXML olduğunda bu verileri geri yükleyebilir. aranan.

Ve bu kadar. Bunu kullanmak için, XmlSerializable'dan XML miras almak için serileştirilmesi gereken herhangi bir türünüz olacaktır. Bu türün serileştirilmesi veya serileştirilmesi gerektiğinde, ToXML veya FromXML'yi çağırmanız yeterlidir. Aslında, XmlSerializable tam teşekküllü bir tür ve polimorfik olduğundan, orijinal türünüz hakkında hiçbir şey bilmeyen, sadece bir dizi XmlSerializable türünü kabul eden bir belge serileştirici oluşturabilirsiniz.

Şimdi, bu senaryoyu, onu karıştıran her sınıfın her yöntem çağrısını günlüğe kaydetmesini sağlayan bir mixin veya onu karıştıran türe işlemlilik sağlayan bir mixin oluşturmak gibi başka şeyler için hayal edin. Liste uzayıp gidebilir.

Bir mixini sadece bir türe bu tür bir etkiyi etkilemeden az miktarda işlevsellik eklemek için tasarlanmış küçük bir baz türü olarak düşünüyorsanız, o zaman altınsınız.

İnşallah. :)


25
Hey, "polimorfik rezonans" ifadesini beğendiniz mi? Kendim uydurdum. Bence. Belki bir yerde fizikte duydum ...
Randolpho

50
İlk cümlenize biraz katılmıyorum. Ruby tek bir miras dilidir ve mixins başka bir sınıftan miras almadan belirli bir sınıfa yöntem eklemenin yoludur.
Keltia

23
@Keltia: Bence karışımlar - tanım gereği - çoklu kalıtım. Ruby durumunda, onlar uygun bir mixin değil bir monkeypatch (veya başka bir şey). Ruby halkı buna bir mixin diyebilir, ama bu farklı bir şey.
S.Lott

10
Aslında, gerçek bir mixin birden fazla miras kullanamaz. Bir mixin, bir sınıftan başka bir sınıftan miras almadan yöntemler, nitelikler vb. İçerir. Bu, kod kullanımının yararlarını polimorfizmle ortaya çıkarmaya eğilimlidir, ancak ebeveynliği (ölüm pırlantası, vb.) Belirleyen sorunları ortadan kaldırır. Şimdi).
Trevor

8
Kayıt için Java, artık varsayılan yöntemlerle mixins'i destekliyor.
shmosel

170

Bu cevap, karışımları aşağıdaki örneklerle açıklamayı amaçlamaktadır :

  • kendine yeten : kısa, örneği anlamak için herhangi bir kütüphaneyi bilmenize gerek yok.

  • Python'da , diğer dillerde değil.

    Terim bu dillerde çok daha yaygın olduğu için Ruby gibi diğer dillerden örnekler olduğu anlaşılabilir, ancak bu bir Python iş parçacığıdır.

Ayrıca tartışmalı soruyu da dikkate alacaktır:

Bir mixini karakterize etmek için birden fazla miras gerekli mi?

Tanımlar

Python'da bir mixin ne olduğunu açıkça söyleyen bir "yetkili" kaynaktan bir alıntı görmedim.

Bir mixinin 2 olası tanımını gördüm (eğer soyut temel sınıflar gibi diğer benzer kavramlardan farklı olarak düşünüleceklerse) ve insanlar hangisinin doğru olduğuna tamamen katılmıyorlar.

Fikir birliği farklı diller arasında değişebilir.

Tanım 1: Çoklu Kalıtım Yok

Bir mixin, sınıfın bazı yöntemlerinin sınıfta tanımlı olmayan bir yöntemi kullanacağı şekilde bir sınıftır.

Bu nedenle, sınıfın somutlaştırılması değil, temel bir sınıf olarak kullanılması amaçlanmıştır. Aksi takdirde, örnek bir istisna oluşturmadan çağrılamayan yöntemlere sahip olacaktır.

Bazı kaynakların eklediği bir kısıtlama, sınıfın veri içermeyebileceği, sadece yöntemler içerebileceği, ancak bunun neden gerekli olduğunu anlamıyorum. Bununla birlikte, pratikte, birçok yararlı karışımın herhangi bir verisi yoktur ve veri içermeyen temel sınıfların kullanımı daha kolaydır.

Klasik bir örnek sadece tüm karşılaştırma operatörleri uygulamasıdır <=ve ==:

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 

Bu özel örnek functools.total_ordering()dekoratör aracılığıyla elde edilebilirdi , ancak buradaki oyun tekerleği yeniden icat etmekti:

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)

Tanım 2: Çoklu Kalıtım

Bir mixin, baz sınıfın bazı yöntemlerinin tanımlanmadığı bir yöntemi kullandığı ve bu yöntemin başka bir baz sınıfı tarafından uygulanması amaçlanan bir tasarım modelidir. Tanım 1'deki gibi türetilmemiş bir .

Terimi mixin sınıfı bu tasarım modelinde kullanılabilir amaçlanan temel sınıfları belirtmektedir (yöntem kullananlar, veya uygulama o todo?)

Belirli bir sınıfın bir mixin olup olmadığına karar vermek kolay değildir: yöntem sadece türetilmiş sınıfta uygulanabilir, bu durumda Tanım 1'e geri dönüyoruz.

Bu model ilginçtir, çünkü farklı temel sınıf seçenekleriyle işlevsellikleri yeniden birleştirmek mümkündür:

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

Yetkili Python olayları

Koleksiyonlar için resmi belgeselde. Abc belgeleri açıkça Mixin Yöntemleri terimini kullanır .

Bir sınıf varsa:

  • uygular __next__
  • tek bir sınıftan miras alır Iterator

daha sonra sınıf ücretsiz bir __iter__ mixin yöntemi alır .

Bu nedenle, en azından dokümantasyonun bu noktasında, mixin çoklu kalıtım gerektirmez ve Tanım 1 ile uyumludur.

Belgeler elbette farklı noktalarda çelişkili olabilir ve diğer önemli Python kütüphaneleri belgelerindeki diğer tanımı kullanıyor olabilirler.

Bu sayfa aynı zamanda terimi kullandığı Set mixinaçıkça sınıfları gibi ileri sürmektedir, Setve IteratorMixin sınıfları çağrılabilir.

Diğer dillerde

  • Ruby: Ruby Programlama ve Ruby Programlama Dili gibi önemli referans kitaplarında belirtildiği gibi, mixin için açıkça birden fazla miras gerektirmez

  • C ++: Uygulanmayan bir yöntem, saf bir sanal yöntemdir.

    Tanım 1, soyut bir sınıfın (saf sanal yöntemi olan bir sınıf) tanımıyla çakışır. Bu sınıf somutlaştırılamaz.

    Tanım 2 sanal kalıtımla mümkündür: Türetilmiş iki sınıftan Çoklu Kalıtım


37

Onları çoklu miras kullanmanın disiplinli bir yolu olarak düşünüyorum - çünkü nihayetinde bir mixin, mixins adı verilen sınıflarla ilgili kuralları takip edebilecek başka bir python sınıfı.

Mixin diyeceğiniz bir şeyi yöneten sözleşmeler hakkındaki anlayışım şu: Mixin:

  • yöntemler ekler ancak örnek değişkenleri eklemez (sınıf sabitleri TAMAM)
  • yalnızca objectPython'dan devralınır

Bu şekilde çoklu kalıtımın potansiyel karmaşıklığını sınırlar ve nereye bakmanız gerektiğini sınırlayarak (tam çoklu kalıtımla karşılaştırıldığında) programınızın akışını izlemeyi oldukça kolaylaştırır. Yakut modüllere benzerler .

Örnek değişkenleri eklemek istersem (tek mirasın izin verdiğinden daha fazla esneklikle) o zaman kompozisyona gitme eğilimindeyim.

Bunu söyledikten sonra, XYZMixin adlı örnek değişkenleri olan sınıflar gördüm.


30

Mixins, Programlamada sınıfın işlevsellik sağladığı bir kavramdır, ancak örnekleme için kullanılması amaçlanmamıştır. Mixins'in temel amacı, bağımsız işlevler sağlamaktır ve mixins'in kendisinin diğer mixins ile miras yoksa ve durumdan kaçınması en iyisidir. Ruby gibi dillerde, bazı doğrudan dil desteği vardır, ancak Python için yoktur. Ancak, Python'da sağlanan işlevselliği yürütmek için çok sınıflı kalıtım kullanabilirsiniz.

Bu videoyu http://www.youtube.com/watch?v=v_uKI2NOLEM izledimMixins'in temellerini anlamak için . Yeni başlayanlar için mixinlerin temellerini ve nasıl çalıştıklarını ve bunları uygularken karşılaşabileceğiniz sorunları anlamak oldukça yararlıdır.

Wikipedia hala en iyisi: http://en.wikipedia.org/wiki/Mixin


29

Bir mixini çoklu kalıtımdan ayıran nedir? Bu sadece bir anlambilim midir?

Bir mixin sınırlı bir çoklu kalıtım şeklidir. Bazı dillerde, bir sınıfa mixin ekleme mekanizması (sözdizimi açısından) kalıtımdakinden biraz farklıdır.

Özellikle Python bağlamında, bir mixin alt sınıflara işlevsellik sağlayan, ancak kendisinin somutlaştırılması amaçlanmayan bir ana sınıftır.

Söylemenize neden olan şey, "bu sadece bir çoklu miras, gerçekten bir karışım değil", bir mixin için karıştırılabilecek sınıfın gerçekten somutlaştırılabileceği ve kullanılabileceğidir - bu yüzden gerçekten semantik ve çok gerçek bir farktır.

Çoklu Kalıtım Örneği

Bu örnek, belgelerden bir OrderedCounter'dır:

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

Hem alt sınıfların Counterve OrderedDictgelen collectionsmodül.

Her ikisi de Counterve OrderedDictkendi başlarına örneklenip kullanılması amaçlanmıştır. Bununla birlikte, her ikisini de alt sınıflandırarak, sipariş edilen ve her nesnedeki kodu yeniden kullanan bir sayacımız olabilir.

Bu, kodu yeniden kullanmanın güçlü bir yoludur, ancak sorunlu da olabilir. Görünüyorsa, nesnelerden birinde bir hata var, dikkatsizce düzeltmek alt sınıfta bir hata oluşturabilir.

Mixin örneği

Mixins genellikle, OrderedCounter gibi çoklu kalıtım kooperatifinin sahip olabileceği potansiyel birleştirme sorunları olmadan kodun yeniden kullanılmasının bir yolu olarak tanıtılır. Mixins kullandığınızda, verilere sıkı sıkıya bağlı olmayan bir işlevsellik kullanırsınız.

Yukarıdaki örnekten farklı olarak, bir karışımın kendi başına kullanılması amaçlanmamıştır. Yeni veya farklı işlevsellik sağlar.

Örneğin, standart kütüphane bir çift var içinde Mixins socketserverkütüphanede .

Her bir sunucu türünün çatallama ve iş parçacığı sürümleri, bu karma sınıflar kullanılarak oluşturulabilir. Örneğin, ThreadingUDPServer aşağıdaki gibi oluşturulur:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

UDPServer'da tanımlanan bir yöntemi geçersiz kıldığından, mix-in sınıfı önce gelir. Çeşitli özniteliklerin ayarlanması, temel sunucu mekanizmasının davranışını da değiştirir.

Bu durumda, mixin yöntemleri UDPServer, eşzamanlılığa izin vermek için nesne tanımındaki yöntemleri geçersiz kılar .

Geçersiz kılınan yöntem gibi görünüyor process_requestve başka bir yöntem de sağlıyor process_request_thread. İşte kaynak kodundan :

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

Çağdaş Bir Örnek

Bu, çoğunlukla gösterim amaçlı olan bir karışımdır - çoğu nesne bu temsilcinin yararlılığının ötesine geçecektir:

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

ve kullanım:

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

Ve kullanım:

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

11

Burada bazı iyi açıklamalar olduğunu düşünüyorum ama başka bir bakış açısı sunmak istedim.

Scala'da, burada tarif edildiği gibi mixins yapabilirsiniz, ancak çok ilginç olan şey, mixinlerin aslında miras alınacak yeni bir sınıf türü oluşturmak için birbirine 'kaynaştırılması'. Özünde, birden fazla sınıftan / karışımdan miras almazsınız, aksine mirasın miras alacağı tüm özelliklerle yeni bir sınıf türü yaratırsınız. Scala şu anda çoklu kalıtımın desteklenmediği JVM'yi temel aldığından (Java 8'den itibaren) bu mantıklıdır. Bu mixin sınıfı türü, bu arada, Scala'da Trait adı verilen özel bir türdür.

Bir sınıfın tanımlanma biçimini ima eder: NewClass sınıfı, FirstMixin'i SecondMixin ile ThirdMixin ile genişletir ...

CPython yorumlayıcısının aynı şeyi yapıp yapmadığından emin değilim (mixin class-Composition) ama şaşırmam. Ayrıca, bir C ++ geçmişinden gelen bir mixin eşdeğeri bir ABC veya 'arayüz' çağırmak olmaz - benzer bir kavram ama kullanım ve uygulamada ıraksak.


9

Etrafında başka bir yol bulabilirseniz (miras yerine kompozisyon veya sadece kendi sınıflarınıza maymun yamalama yöntemleri gibi) bulabilirseniz, yeni Python kodundaki karışık girişlere karşı öneriyorum. çaba.

Eski stil sınıflarında, mix-in'leri başka bir sınıftan birkaç yöntemi yakalamanın bir yolu olarak kullanabilirsiniz. Ancak yeni stil dünyasında her şey, hatta karışıklık bile miras object. Bu, çoklu kalıtımın herhangi bir şekilde kullanılmasının doğal olarak MRO sorunlarını ortaya çıkardığı anlamına gelir .

Çoklu kalıtım MRO'yu Python'da, özellikle super () işlevini çalıştırmanın yolları vardır, ancak bu, super () kullanarak tüm sınıf hiyerarşinizi yapmanız gerektiği anlamına gelir ve kontrol akışını anlamak çok daha zordur.


3
Sürüm 2.3'ten beri Python, Python 2.3 Yöntem Çözünürlük Sırası veya Yöntem Çözünürlük Sırası'nda açıklanan "C3 yöntem çözünürlüğünü" kullandığından .
webwurst

11
Şahsen, çoğu durumda maymun yaması üzerine karışımları alırdım; kod hakkında akıl yürütmek ve takip etmek daha kolaydır.
tdammers

5
Downvoted. Cevabınız geliştirme stilleri hakkında geçerli bir görüş ifade ederken, asıl soruyu gerçekten ele almıyorsunuz.
Ryan B. Lynch

8

Belki birkaç örnek yardımcı olacaktır.

Bir sınıf oluşturuyorsanız ve bunun sözlük gibi davranmasını istiyorsanız, __ __gerekli tüm çeşitli yöntemleri tanımlayabilirsiniz . Ama bu biraz acı verici. Alternatif olarak, sadece birkaç tanesini tanımlayabilir ve (diğer herhangi bir mirasa ek olarak) 'den UserDict.DictMixin( collections.DictMixinpy3k'ye taşınmış ) devralınabilirsiniz . Bu, sözlük API'sinin geri kalanını otomatik olarak tanımlama etkisine sahip olacaktır.

İkinci bir örnek: wxPython GUI araç seti, birden çok sütunlu (örneğin, Windows Gezgini'nde dosya görüntüleme gibi) liste denetimleri yapmanızı sağlar. Varsayılan olarak, bu listeler oldukça basittir. Sütun başlığına tıklayarak, ListCtrl'den devralınarak ve uygun karışımlar ekleyerek listeyi belirli bir sütuna göre sıralama yeteneği gibi ek işlevler ekleyebilirsiniz.


8

Bu bir Python örneği değildir, ancak D programlama dilinde terim mixin, aynı şekilde kullanılan bir yapıya atıfta bulunmak için kullanılır; bir sınıfa bir yığın malzeme ekleyerek.

D'de (bu arada MI yapmaz), bu bir kapsama bir şablon (sözdizimsel olarak farkında ve güvenli makrolar düşünün ve yakın olacaksınız) ekleyerek yapılır. Bu, bir sınıf, yapı, işlev, modül veya herhangi bir sayıda bildirime genişletilecek herhangi bir şeydeki tek bir kod satırına izin verir.


2
Mixin, D, Ruby, vb.'de kullanılan genel bir terimdir. Wikipedia'ya göre, bunlar eski okul lisp sistemlerinden kaynaklandı ve ilk olarak 1983'te belgelendi: en.wikipedia.org/wiki/…
Lee B

7

OP, C ++ 'da hiç mixin duymadığını, belki de C ++' da Merakla Yinelenen Şablon Deseni (CRTP) olarak adlandırıldığından bahsetti. Ayrıca, @Ciro Santilli, mixinin C ++ 'da soyut temel sınıf yoluyla uygulandığını belirtti. Soyut temel sınıf, mixin'i uygulamak için kullanılabilse de, çalışma zamanında sanal işlevin işlevselliği, çalışma zamanında sanal tablo araması yükü olmadan derleme zamanında şablon kullanılarak elde edilebildiğinden, aşırı bir iştir.

CRTP modeli burada ayrıntılı olarak açıklanmaktadır

@Ciro Santilli'nin cevabındaki python örneğini aşağıdaki şablon sınıfını kullanarak C ++ 'a dönüştürdüm:

    #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: ComparableMixin içinde korumalı kurucu eklendi, böylece yalnızca miras alınabilir ve somutlaştırılamaz. ComparableMixin nesnesi oluşturulduğunda korumalı kurucunun derleme hatasına nasıl neden olacağını göstermek için örnek güncellendi.


C ++ 'da karışımlar ve CRTP tam olarak aynı şey değildir.
ashrasmun

6

Belki yakuttan bir örnek yardımcı olabilir:

Mixin'i dahil edebilir Comparableve bir işlevi tanımlayabilirsiniz "<=>(other)", mixin tüm bu işlevleri sağlar:

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

Bunu <=>(other)doğru sonucu çağırarak ve geri vererek yapar .

"instance <=> other"her iki nesne de eşitse 0, daha büyükse 0'dan instanceküçük otherve daha büyükse 0'dan büyük döndürür other.


İşte Python için benzer bir mixin sağlayan bir yazı. Öneri __lt__bunun yerine temel olarak tanımlansa da __cmp__, ikincisi aslında kullanımdan kaldırılmış ve kullanımı önerilmez. Bana göre bu mixin'i oldukça karmaşık dekoratörler ( functools'un bir parçası ) yerine kullanmak daha kolay görünüyor - bu , hangi karşılaştırmaların sağlandığına daha dinamik olarak tepki
verebilse de

6

mixin bir sınıfa işlevsellik eklemek için bir yol sağlar, yani modülü istenen sınıfın içine dahil ederek bir modülde tanımlanan yöntemlerle etkileşime girebilirsiniz. Her ne kadar yakut çoklu mirası desteklemese de, buna ulaşmak için bir alternatif olarak mixin sağlar.

Burada, mixin kullanarak çoklu kalıtımın nasıl elde edildiğini açıklayan bir örnek verilmiştir.

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
Bu ve genel olarak çoklu kalıtım arasındaki fark nedir?
Ciro Santilli

Aradaki fark, modüllerden örnekler oluşturamamanızdır, ancak genel sınıflar ve modüller arasında bir ayrım yoksa, karışımlar açık bir şey değildir ve genel bir sınıfın nerede ve bir
karışımın

Yani Ruby'de mixins sadece örneklenemeyen ancak birden fazla miras için kullanılması gereken sınıflardır?
Trilarion

6

Ben sadece python karıştırıcılar için birim test uygulamak için bir python mixin kullandım. Normalde, bir milter bir MTA ile konuşarak birim testini zorlaştırır. Test karışımı MTA ile konuşan ve test senaryoları tarafından yönlendirilen simüle edilmiş bir ortam yaratan yöntemleri geçersiz kılar.

Yani, spfmilter gibi değiştirilmemiş bir milter uygulaması ve TestBase'i karıştırın, şöyle:

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)

Ardından, milter uygulaması için test örneklerinde TestMilter kullanın:

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

Önceki yanıtların MixIns'ın ne olduğunu çok iyi tanımladığını düşünüyorum . Ancak, onları daha iyi anlamak için, kod / uygulama perspektifinden MixIns'ı Soyut Sınıflar ve Arayüzler ile karşılaştırmak yararlı olabilir :

1. Soyut Sınıf

  • Bir veya daha fazla soyut yöntem içermesi gereken sınıf

  • Özet sınıfı olabilir durumuna (örnek değişkenler) ve non-soyut yöntemler içerir

2. Arayüz

  • Arabirim yalnızca soyut yöntemler içerir (soyut olmayan yöntemler ve dahili durum yoktur)

3. MixIns

  • Katmalar (Arayüz gibi) yok dahili durumunu içerirler (örneğin değişkenleri)
  • Katmalar bir veya daha fazla-soyut yöntemleri (içerdikleri için arabirimler farklı olmayan arka yöntemler içerir)

Örneğin, Python'da bunlar sadece sözleşmelerdir, çünkü yukarıdakilerin tümü classes olarak tanımlanmıştır . Ancak, her iki ortak özelliği Özet Sınıflar, Arayüz ve Katmalar onlar olmasıdır olmamalıdır kendi başlarına var, yani örneği alınmamalıdır.


3

Ac # arka planınız olduğunu okudum. İyi bir başlangıç ​​noktası .NET için bir mixin uygulaması olabilir.

Codeplex projesine şu adresten bakmak isteyebilirsiniz: http://remix.codeplex.com/

Genel bir bakış için lang.net Sempozyumu bağlantısını izleyin. Codeplex sayfasında dokümantasyon konusunda daha fazlası var.

Saygılarımla Stefan

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.