¿Qué es un mixin y por qué son útiles?

1135

En " Programming Python ", Mark Lutz menciona "mixins". Vengo de C / C ++ / C # y no había escuchado el término antes. ¿Qué es un mixin?

Al leer entre líneas de este ejemplo (al que me he vinculado porque es bastante largo), supongo que se trata de un caso de uso de herencia múltiple para extender una clase en lugar de una subclase "adecuada". ¿Es esto correcto?

¿Por qué querría hacer eso en lugar de poner la nueva funcionalidad en una subclase? De hecho, ¿por qué sería mejor un enfoque de herencia mixta / múltiple que usar la composición?

¿Qué separa un mixin de la herencia múltiple? ¿Es solo una cuestión de semántica?

0
830

Un mixin es un tipo especial de herencia múltiple. Hay dos situaciones principales en las que se utilizan mixins:

  1. Desea proporcionar muchas funciones opcionales para una clase.
  2. Desea utilizar una función en particular en muchas clases diferentes.

Para ver un ejemplo del número uno, considere el sistema de solicitud y respuesta de werkzeug . Puedo hacer un objeto de solicitud simple y antiguo diciendo:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Si quiero agregar la compatibilidad con el encabezado de aceptación, lo haría

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Si quisiera crear un objeto de solicitud que admita aceptar encabezados, etags, autenticación y soporte de agente de usuario, podría hacer esto:

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

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

La diferencia es sutil, pero en los ejemplos anteriores, las clases de mezcla no se hicieron para que funcionen por sí solas. En la herencia múltiple más tradicional, AuthenticationMixin(por ejemplo) probablemente sería algo más parecido a Authenticator. Es decir, la clase probablemente estaría diseñada para ser independiente.

3
  • 156
    Una tercera situación es: desea proporcionar muchas características (no opcionales) para una clase, pero desea las características en clases separadas (y en módulos separados) para que cada módulo se trate de una característica (comportamiento). IOW, no para reutilización, pero para compartimentación.
    bootchk
    12 de septiembre de 2013 a las 12:38
  • 72
    Probablemente no sea un problema en este ejemplo, pero generalmente desea poner la clase base principal como el último elemento entre paréntesis para crear la cadena de herencia: Request ==> Mixin ==> ... ==> BaseRequest. Vea aquí: ianlewis.org/en/mixins-and-python
    hillel
    14 de junio de 2014 a las 12:21
  • 12
    @hillel es un buen punto, pero tenga en cuenta que Python llamará a los métodos de las superclases de izquierda a derecha (cuando necesite anular el constructor, por ejemplo). 16 de junio de 2015 a las 8:23
268

Primero, debe tener en cuenta que los mixins solo existen en lenguajes de herencia múltiple. No puede hacer una mezcla en Java o C #.

Básicamente, un mixin es un tipo de base independiente que proporciona una funcionalidad limitada y resonancia polimórfica para una clase infantil. Si está pensando en C #, piense en una interfaz que no tenga que implementar porque ya está implementada; simplemente hereda de él y se beneficia de su funcionalidad.

Los mixins suelen tener un alcance limitado y no deben extenderse.

[editar - en cuanto a por qué:]

Supongo que debería abordar el motivo, ya que lo preguntaste. El gran beneficio es que no tiene que hacerlo usted mismo una y otra vez. En C #, el lugar más importante donde un mixin podría beneficiarse podría ser el patrón Disposal . Siempre que implementa IDisposable, casi siempre desea seguir el mismo patrón, pero termina escribiendo y reescribiendo el mismo código básico con variaciones menores. Si hubiera una mezcla de eliminación extensible, podría ahorrarse una gran cantidad de escritura adicional.

[editar 2 - para responder a sus otras preguntas]

What separates a mixin from multiple inheritance? Is it just a matter of semantics?

Si. La diferencia entre un mixin y una herencia múltiple estándar es solo una cuestión de semántica; una clase que tiene herencia múltiple puede utilizar un mixin como parte de esa herencia múltiple.

El objetivo de un mixin es crear un tipo que se pueda "mezclar" con cualquier otro tipo a través de la herencia sin afectar el tipo heredado y al mismo tiempo ofrecer algunas funciones beneficiosas para ese tipo.

Nuevamente, piense en una interfaz que ya esté implementada.

Personalmente, no uso mixins ya que desarrollo principalmente en un idioma que no los admite, por lo que me está costando mucho encontrar un ejemplo decente que simplemente proporcione ese "¡ajá!" momento para ti. Pero lo intentaré de nuevo. Voy a usar un ejemplo que es artificial, la mayoría de los lenguajes ya brindan la función de una forma u otra, pero eso, con suerte, explicará cómo se supone que se crean y usan los mixins. Aquí va:

Suponga que tiene un tipo que desea poder serializar hacia y desde XML. Desea que el tipo proporcione un método "ToXML" que devuelva una cadena que contenga un fragmento XML con los valores de datos del tipo y un "FromXML" que permita al tipo reconstruir sus valores de datos a partir de un fragmento XML en una cadena. Nuevamente, este es un ejemplo artificial, por lo que tal vez use un flujo de archivos o una clase XML Writer de la biblioteca de tiempo de ejecución de su idioma ... lo que sea. El punto es que desea serializar su objeto a XML y obtener un nuevo objeto de XML.

El otro punto importante de este ejemplo es que desea hacer esto de forma genérica. No desea tener que implementar un método "ToXML" y "FromXML" para cada tipo que desea serializar, desea algunos medios genéricos para asegurarse de que su tipo haga esto y simplemente funcione. Quieres reutilizar el código.

Si su idioma lo admite, puede crear el mixin XmlSerializable para que haga el trabajo por usted. Este tipo implementaría los métodos ToXML y FromXML. Usando algún mecanismo que no es importante para el ejemplo, sería capaz de recopilar todos los datos necesarios de cualquier tipo con el que esté mezclado para construir el fragmento XML devuelto por ToXML y sería igualmente capaz de restaurar esos datos cuando FromXML es llamado.

Y eso es. Para usarlo, tendría cualquier tipo que deba serializarse a XML heredado de XmlSerializable. Siempre que necesite serializar o deserializar ese tipo, simplemente llamaría a ToXML o FromXML. De hecho, dado que XmlSerializable es un tipo completo y polimórfico, posiblemente podría construir un serializador de documentos que no sepa nada sobre su tipo original, aceptando solo, digamos, una matriz de tipos XmlSerializable.

Ahora imagina usar este escenario para otras cosas, como crear un mixin que asegure que cada clase que lo mezcla registre cada llamada de método, o un mixin que proporcione transaccionalidad al tipo que lo mezcla. La lista puede seguir y seguir.

Si solo piensa en un mixin como un tipo de base pequeño diseñado para agregar una pequeña cantidad de funcionalidad a un tipo sin afectar de otra manera ese tipo, entonces está bien.

Ojalá. :)

20
  • 25
    Oye, ¿te gusta la frase "resonancia polimórfica"? Lo inventé yo mismo. Creo. Tal vez lo escuché en física en alguna parte ... 10 feb 09 a las 19:01
  • 52
    No estoy de acuerdo con tu primera oración. Ruby es un lenguaje de herencia única y los mixins son la forma de agregar métodos a una clase determinada sin heredar de otra clase.
    Keltia
    10 feb 09 a las 19:11
  • 25
    @Keltia: Creo que los mixin son, por definición, herencia múltiple. En el caso de Ruby, son un parche de mono (o algo más), no una mezcla adecuada. La gente de Ruby puede llamarlo un mixin, pero es un tipo de cosas diferente.
    S.Lott
    10 feb 09 a las 19:47
  • 11
    En realidad, un verdadero mixin no puede usar herencia múltiple. Un mixin incluye métodos, atributos, etc. de una clase en otra sin heredarlo. Esto tiende a hacer que los beneficios de la reutilización del código aparezcan con polimorfismo, pero omite los problemas que determinan el parentesco (el diamante de la muerte, etc.). Los lenguajes de soporte de Mixin también tienden a permitir la inclusión parcial de la clase mixin (las cosas empiezan a sonar un poco como aspectos ahora).
    Trevor
    9 de mayo de 2013 a las 20:09
  • 12
    Para el registro, Java ahora admite mixins con métodos predeterminados.
    shmosel
    17 de agosto de 2015 a las 23:34
211

Esta respuesta tiene como objetivo explicar los mixins con ejemplos que son:

  • autónomo : breve, sin necesidad de conocer ninguna biblioteca para comprender el ejemplo.

  • en Python , no en otros lenguajes.

    Es comprensible que haya ejemplos de otros lenguajes como Ruby ya que el término es mucho más común en esos lenguajes, pero este es un hilo de Python .

También considerará la controvertida cuestión:

Is multiple inheritance necessary or not to characterize a mixin?

Definiciones

Todavía tengo que ver una cita de una fuente "autorizada" que diga claramente qué es un mixin en Python.

He visto 2 posibles definiciones de un mixin (si deben considerarse diferentes de otros conceptos similares, como clases base abstractas), y la gente no está del todo de acuerdo sobre cuál es la correcta.

El consenso puede variar entre diferentes idiomas.

Definición 1: sin herencia múltiple

Un mixin es una clase tal que algún método de la clase usa un método que no está definido en la clase.

Por lo tanto, la clase no está destinada a ser instanciada, sino que sirve como clase base. De lo contrario, la instancia tendría métodos que no se pueden llamar sin generar una excepción.

Una restricción que agregan algunas fuentes es que la clase puede no contener datos, solo métodos, pero no veo por qué esto es necesario. Sin embargo, en la práctica, muchos mixins útiles no tienen ningún dato, y las clases base sin datos son más fáciles de usar.

Un ejemplo clásico es la implementación de todos los operadores de comparación de solo <=y ==:

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 

Este ejemplo en particular podría haberse logrado a través del functools.total_ordering()decorador, pero el juego aquí fue reinventar la rueda:

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)

Definición 2: herencia múltiple

Un mixin es un patrón de diseño en el que algún método de una clase base usa un método que no define, y ese método está destinado a ser implementado por otra clase base , no por el derivado como en la Definición 1.

El término clase mixin se refiere a las clases base que están destinadas a ser utilizadas en ese patrón de diseño (¿TODO las que usan el método o las que lo implementan?)

No es fácil decidir si una clase dada es un mixin o no: el método podría simplemente implementarse en la clase derivada, en cuyo caso volvemos a la Definición 1. Tienes que considerar las intenciones del autor.

Este patrón es interesante porque es posible recombinar funcionalidades con diferentes opciones de clases base:

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

Apariciones autoritativas de Python

En la documentación oficial de collections.abc, la documentación utiliza explícitamente el término Métodos Mixin .

Dice que si una clase:

  • implementos __next__
  • hereda de una sola clase Iterator

entonces la clase obtiene un __iter__ método mixin gratis.

Por lo tanto, al menos en este punto de la documentación, mixin no requiere herencia múltiple y es coherente con la Definición 1.

La documentación, por supuesto, podría ser contradictoria en diferentes puntos, y otras bibliotecas importantes de Python podrían estar usando la otra definición en su documentación.

Esta página también usa el término Set mixin, que sugiere claramente que las clases como Sety Iteratorpueden llamarse clases Mixin.

En otros idiomas

  • Ruby: Claramente, no requiere herencia múltiple para mezclar, como se menciona en los principales libros de referencia como Programming Ruby y The Ruby Programming Language.

  • C ++: un virtualmétodo que se establece =0es un método virtual puro.

    La definición 1 coincide con la definición de una clase abstracta (una clase que tiene un método virtual puro). No se puede crear una instancia de esa clase.

    La definición 2 es posible con herencia virtual: herencia múltiple de dos clases derivadas

0
42

Pienso en ellos como una forma disciplinada de usar la herencia múltiple, porque en última instancia, un mixin es solo otra clase de Python que (podría) seguir las convenciones sobre las clases que se llaman mixins.

Mi comprensión de las convenciones que gobiernan algo que llamarías Mixin es que un Mixin:

  • agrega métodos pero no variables de instancia (las constantes de clase están bien)
  • solo hereda de object(en Python)

De esa manera, limita la complejidad potencial de la herencia múltiple y hace que sea razonablemente fácil rastrear el flujo de su programa al limitar dónde debe buscar (en comparación con la herencia múltiple completa). Son similares a los módulos ruby .

Si quiero agregar variables de instancia (con más flexibilidad de la que permite la herencia única), tiendo a optar por la composición.

Habiendo dicho eso, he visto clases llamadas XYZMixin que tienen variables de instancia.

0
35

What separates a mixin from multiple inheritance? Is it just a matter of semantics?

Un mixin es una forma limitada de herencia múltiple. En algunos lenguajes, el mecanismo para agregar un mixin a una clase es ligeramente diferente (en términos de sintaxis) al de la herencia.

En el contexto de Python especialmente, un mixin es una clase padre que proporciona funcionalidad a subclases pero no está destinada a ser instanciada en sí misma.

Lo que podría hacer que digas, "eso es solo herencia múltiple, no realmente un mixin" es si la clase que podría confundirse con un mixin realmente se puede instanciar y usar, por lo que de hecho es una diferencia semántica y muy real.

Ejemplo de herencia múltiple

Este ejemplo, de la documentación , es un 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),)

Subclasifica tanto the Countercomo the OrderedDictdel collectionsmódulo.

Ambos Countery OrderedDictestán destinados a ser instanciados y utilizados por sí mismos. Sin embargo, al subclasificarlos a ambos, podemos tener un contador que se ordena y reutiliza el código en cada objeto.

Esta es una forma poderosa de reutilizar código, pero también puede ser problemática. Si resulta que hay un error en uno de los objetos, arreglarlo sin cuidado podría crear un error en la subclase.

Ejemplo de un Mixin

Los mixins generalmente se promocionan como la forma de obtener la reutilización del código sin posibles problemas de acoplamiento que podría tener la herencia múltiple cooperativa, como el OrderedCounter. Cuando usa mixins, usa una funcionalidad que no está tan estrechamente acoplada a los datos.

A diferencia del ejemplo anterior, un mixin no está diseñado para usarse por sí solo. Proporciona una funcionalidad nueva o diferente.

Por ejemplo, la biblioteca estándar tiene un par de mixins en la socketserverbiblioteca .

Forking and threading versions of each type of server can be created using these mix-in classes. For instance, ThreadingUDPServer is created as follows:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

The mix-in class comes first, since it overrides a method defined in UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.

En este caso, los métodos mixin anulan los métodos en la UDPServerdefinición del objeto para permitir la simultaneidad.

El método reemplazado parece ser process_requesty también proporciona otro método, process_request_thread. Aquí está del código fuente :

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

Un ejemplo elaborado

Este es un mixin que es principalmente para fines de demostración; la mayoría de los objetos evolucionarán más allá de la utilidad de esta repr:

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

y el uso sería:

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

Y uso:

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

Mixins es un concepto en programación en el que la clase proporciona funcionalidades pero no está destinado a ser utilizado para la creación de instancias. El objetivo principal de Mixins es proporcionar funcionalidades que sean independientes y sería mejor si los mixins en sí no tuvieran herencia con otros mixins y también evitaran el estado. En lenguajes como Ruby, hay algún soporte de lenguaje directo, pero para Python, no lo hay. Sin embargo, puede utilizar la herencia de varias clases para ejecutar la funcionalidad proporcionada en Python.

Vi este video http://www.youtube.com/watch?v=v_uKI2NOLEM para comprender los conceptos básicos de los mixins. Es muy útil para un principiante comprender los conceptos básicos de los mixins y cómo funcionan y los problemas que podría enfrentar al implementarlos.

Wikipedia sigue siendo la mejor: http://en.wikipedia.org/wiki/Mixin

0
18

Creo que las respuestas anteriores definieron muy bien qué son los Mixins . Sin embargo, para comprenderlos mejor, podría ser útil comparar MixIns con clases e interfaces abstractas desde la perspectiva del código / implementación:

1. Clase abstracta

  • Clase que debe contener uno o más métodos abstractos

  • La clase abstracta puede contener estados (variables de instancia) y métodos no abstractos

2. Interfaz

  • La interfaz contiene solo métodos abstractos (sin métodos no abstractos ni estado interno)

3. Mezclas

  • MixIns (como interfaces) no contienen estado interno (variables de instancia)
  • Los MixIns contienen uno o más métodos no abstractos ( pueden contener métodos no abstractos a diferencia de las interfaces)

En, por ejemplo, Python, estas son solo convenciones, porque todas las anteriores se definen como classes. Sin embargo, la característica común de las clases abstractas, las interfaces y los mixins es que no deberían existir por sí solas, es decir, no deberían crearse una instancia.

11

Creo que ha habido algunas buenas explicaciones aquí, pero quería ofrecer otra perspectiva.

En Scala, puede hacer mixins como se ha descrito aquí, pero lo que es muy interesante es que los mixins en realidad se 'fusionan' para crear un nuevo tipo de clase de la que heredar. En esencia, no hereda de múltiples clases / mixins, sino que genera un nuevo tipo de clase con todas las propiedades del mixin para heredar. Esto tiene sentido ya que Scala se basa en la JVM donde actualmente no se admite la herencia múltiple (a partir de Java 8). Este tipo de clase mixin, por cierto, es un tipo especial llamado Rasgo en Scala.

Se insinúa en la forma en que se define una clase: la clase NewClass extiende FirstMixin con SecondMixin con ThirdMixin ...

No estoy seguro de si el intérprete de CPython hace lo mismo (mezclando composición de clase) pero no me sorprendería. Además, viniendo de un fondo de C ++, no llamaría ABC o 'interfaz' equivalente a un mixin - es un concepto similar pero divergente en uso e implementación.

9

Aconsejaría contra las mezclas en el nuevo código de Python, si puede encontrar alguna otra forma de evitarlo (como composición en lugar de herencia, o simplemente métodos de parcheo de mono en sus propias clases) que no es mucho más esfuerzo.

En las clases de estilo antiguo, podría usar mezclas como una forma de obtener algunos métodos de otra clase. Pero en el mundo del nuevo estilo todo hereda, incluso la mezcla object. Eso significa que cualquier uso de herencia múltiple naturalmente presenta problemas de MRO .

Hay formas de hacer que MRO de herencia múltiple funcione en Python, sobre todo la función super (), pero significa que tiene que hacer toda la jerarquía de clases usando super (), y es considerablemente más difícil entender el flujo de control.

3
  • 3
    Desde la versión 2.3, Python usa la "resolución del método C3" explicada en El orden de resolución de método de Python 2.3 u Orden de resolución de método . 13/03/12 a las 14:11
  • 12
    Personalmente, en la mayoría de los casos, preferiría los mixins a los parches de monos; es más fácil razonar y seguir el código. 22/04/13 a las 9:31
  • 5
    Voto en contra. Si bien su respuesta expresa una opinión válida sobre los estilos de desarrollo, en realidad no aborda la pregunta real. 13 de septiembre de 2013 a las 15:23
9

Quizás un par de ejemplos ayuden.

Si está creando una clase y desea que actúe como un diccionario, puede definir todos los __ __métodos necesarios. Pero eso es un poco molesto. Como alternativa, puede definir algunos y heredar (además de cualquier otra herencia) de UserDict.DictMixin(trasladado a collections.DictMixinen py3k). Esto tendrá el efecto de definir automáticamente todo el resto de la API del diccionario.

Un segundo ejemplo: el kit de herramientas GUI wxPython le permite crear controles de lista con múltiples columnas (como, por ejemplo, la visualización del archivo en el Explorador de Windows). Por defecto, estas listas son bastante básicas. Puede agregar funcionalidad adicional, como la capacidad de ordenar la lista por una columna en particular haciendo clic en el encabezado de la columna, heredando de ListCtrl y agregando los mixins apropiados.

9

No es un ejemplo de Python, pero en el lenguaje de programación D el término mixinse usa para referirse a una construcción que se usa de la misma manera; agregar un montón de cosas a una clase.

En D (que por cierto no hace MI), esto se hace insertando una plantilla (piense en macros sintácticamente conscientes y seguras y estará cerca) en un alcance. Esto permite que una sola línea de código en una clase, estructura, función, módulo o lo que sea se expanda a cualquier número de declaraciones.

1
  • 2
    Mixin es un término general, utilizado en D, Ruby, etc. Según Wikipedia, se originaron en los sistemas lisp de la vieja escuela y se documentaron por primera vez en 1983: en.wikipedia.org/wiki/…
    Lee B
    26/04/10 a las 13:19
7

OP mencionó que nunca había oído hablar de mixin en C ++, quizás eso se debe a que en C ++ se les llama Curious Recurring Template Pattern (CRTP). Además, @Ciro Santilli mencionó que mixin se implementa a través de una clase base abstracta en C ++. Si bien la clase base abstracta se puede usar para implementar mixin, es una exageración ya que la funcionalidad de la función virtual en el tiempo de ejecución se puede lograr usando la plantilla en el tiempo de compilación sin la sobrecarga de la búsqueda de la tabla virtual en el tiempo de ejecución.

El patrón CRTP se describe en detalle aquí.

He convertido el ejemplo de Python en la respuesta de @Ciro Santilli a C ++ usando la clase de plantilla a continuación:

    #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;
}

EDITAR: Se agregó un constructor protegido en ComparableMixin para que solo se pueda heredar y no instanciar. Se actualizó el ejemplo para mostrar cómo el constructor protegido provocará un error de compilación cuando se cree un objeto de ComparableMixin.

1
  • Mixins y CRTP no son exactamente lo mismo en C ++. 19/02/19 a las 10:14
6

Quizás un ejemplo de ruby ​​pueda ayudar:

Puede incluir el mixin Comparabley definir una función "<=>(other)", el mixin proporciona todas esas funciones:

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

Lo hace invocando <=>(other)y devolviendo el resultado correcto.

"instance <=> other"devuelve 0 si ambos objetos son iguales, menor que 0 si instancees mayor que othery mayor que 0 si otheres mayor.

1
  • Aquí hay una publicación que proporciona un mixin similar para Python. Aunque la sugerencia se define __lt__como base en lugar de __cmp__, la última de las cuales está en desuso y se desaconseja su uso. Para mí, parece más simple usar ese mixin en lugar de decoradores bastante complicados (parte de functools ), aunque este puede reaccionar de manera más dinámica sobre qué comparaciones se proporcionan ... 12 abr 13 a las 9:03
6

mixin ofrece una forma de agregar funcionalidad en una clase, es decir, puede interactuar con los métodos definidos en un módulo al incluir el módulo dentro de la clase deseada. Aunque ruby ​​no admite herencia múltiple, proporciona mixin como alternativa para lograrlo.

aquí hay un ejemplo que explica cómo se logra la herencia múltiple usando 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
3
  • 4
    ¿Cuál es la diferencia entre esto y la herencia múltiple en general? 13/11/2013 a las 10:13
  • La diferencia es que no puede crear instancias a partir de módulos, pero si no hay diferenciación entre clases generales y módulos, los mixins no son algo explícito y es difícil entender dónde está una clase general y dónde está un mixin.
    ka8725
    6 de enero de 2014 a las 23:30
  • Entonces, en Ruby, los mixins son solo clases que no se pueden instanciar pero que deben usarse para herencia múltiple. 4 abr 14 a las 11:15
6

Acabo de usar una mezcla de python para implementar pruebas unitarias para python milters. Normalmente, un milter habla con un MTA, lo que dificulta las pruebas unitarias. La mezcla de prueba anula los métodos que se comunican con el MTA y, en su lugar, crea un entorno simulado impulsado por casos de prueba.

Entonces, toma una aplicación milter no modificada, como spfmilter, y mezcla TestBase, así:

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)

Luego, use TestMilter en los casos de prueba para la aplicación milter:

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='[email protected]')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

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

3

Leí que tienes antecedentes de c #. Por lo tanto, un buen punto de partida podría ser una implementación mixta para .NET.

Es posible que desee consultar el proyecto codeplex en http://remix.codeplex.com/

Vea el enlace del Simposio de lang.net para obtener una descripción general. Aún queda más por venir en la documentación de la página del codeplex.

saludos Stefan

0