Esta respuesta tiene como objetivo explicar los mixins con ejemplos que son:
autocontenido : breve, sin necesidad de conocer ninguna biblioteca para comprender el ejemplo.
en Python , no en otros idiomas.
Es comprensible que haya ejemplos de otros idiomas, como Ruby, ya que el término es mucho más común en esos idiomas, pero este es un hilo de Python .
También considerará la controvertida pregunta:
¿Es necesaria la herencia múltiple o no para caracterizar un 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 se consideran diferentes de otros conceptos similares, como las clases base abstractas), y las personas no están totalmente 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 debe ser instanciada, sino que sirve como una clase base. De lo contrario, la instancia tendría métodos que no se pueden invocar 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 datos, 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 usarse en ese patrón de diseño (¿TODOS los que usan el método o los que lo implementan?)
No es fácil decidir si una clase dada es una combinación o no: el método podría implementarse solo en la clase derivada, en cuyo caso volvemos a la Definición 1. Debe 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
Ocurrencias autorizadas de Python
En la documentación oficial de collections.abc, la documentación utiliza explícitamente el término Métodos Mixin .
Establece 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 podría ser, por supuesto, contradictoria en diferentes puntos, y otras bibliotecas importantes de Python podrían estar utilizando la otra definición en su documentación.
Esta página también usa el término Set mixin
, que sugiere claramente que a las clases les gustan Set
y Iterator
pueden llamarse clases Mixin.
En otros idiomas
Ruby: Claramente no requiere herencia múltiple para mixin, como se menciona en los principales libros de referencia como Programming Ruby y The Ruby programación Language
C ++: un método que no se implementa es 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). Esa clase no puede ser instanciada.
La definición 2 es posible con herencia virtual: herencia múltiple de dos clases derivadas