Esta resposta tem como objetivo explicar os mixins com exemplos que são:
independente : curto, sem a necessidade de conhecer nenhuma biblioteca para entender o exemplo.
em Python , não em outros idiomas.
É compreensível que houvesse exemplos de outras linguagens, como Ruby, já que o termo é muito mais comum nessas linguagens, mas esse é um encadeamento Python .
Também deve considerar a questão controversa:
A herança múltipla é necessária ou não para caracterizar um mixin?
Definições
Ainda não vi uma citação de uma fonte "autorizada" dizendo claramente o que é um mixin no Python.
Eu já vi duas definições possíveis de um mixin (se elas devem ser consideradas diferentes de outros conceitos semelhantes, como classes básicas abstratas), e as pessoas não concordam inteiramente sobre qual delas está correta.
O consenso pode variar entre diferentes idiomas.
Definição 1: nenhuma herança múltipla
Um mixin é uma classe de tal forma que algum método da classe usa um método que não está definido na classe.
Portanto, a classe não deve ser instanciada, mas serve como uma classe base. Caso contrário, a instância teria métodos que não podem ser chamados sem gerar uma exceção.
Uma restrição que algumas fontes adicionam é que a classe pode não conter dados, apenas métodos, mas não vejo por que isso é necessário. Na prática, no entanto, muitos mixins úteis não possuem dados e as classes base sem dados são mais simples de usar.
Um exemplo clássico é a implementação de todos os operadores de comparação apenas <=
e ==
:
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 exemplo em particular poderia ter sido alcançado através do functools.total_ordering()
decorador, mas o jogo aqui era reinventar a 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)
Definição 2: herança múltipla
Um mixin é um padrão de design no qual algum método de uma classe base usa um método que não define, e esse método deve ser implementado por outra classe base , não pelo derivado, como na Definição 1.
O termo classe mixin refere-se a classes base que devem ser usadas nesse padrão de design (TODO aquelas que usam o método ou aquelas que o implementam?)
Não é fácil decidir se uma determinada classe é uma combinação ou não: o método pode ser implementado apenas na classe derivada; nesse caso, voltamos à definição 1. Você deve considerar as intenções do autor.
Esse padrão é interessante porque é possível recombinar funcionalidades com diferentes opções de classes 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
Ocorrências oficiais do Python
Na documentação oficial para coleções.abc, a documentação usa explicitamente o termo Métodos Mixin .
Ele afirma que se uma classe:
- implementa
__next__
- herda de uma única classe
Iterator
então a classe recebe um __iter__
método mixin de graça.
Portanto, pelo menos neste ponto da documentação, mixin não requer herança múltipla e é coerente com a Definição 1.
É claro que a documentação pode ser contraditória em pontos diferentes, e outras bibliotecas Python importantes podem estar usando a outra definição em sua documentação.
Esta página também usa o termo Set mixin
, que sugere claramente que as classes gostam Set
e Iterator
podem ser chamadas de classes Mixin.
Em outros idiomas
Ruby: Claramente não requer herança múltipla para mixin, como mencionado nos principais livros de referência, como Programming Ruby e The Ruby programming Language
C ++: um método que não é implementado é um método virtual puro.
A definição 1 coincide com a definição de uma classe abstrata (uma classe que possui um método virtual puro). Essa classe não pode ser instanciada.
A definição 2 é possível com herança virtual: herança múltipla de duas classes derivadas