Estou plenamente ciente de que pylint
outras ferramentas de análise estática não são oniscientes e, às vezes, seus conselhos devem ser desobedecidos. (Isso se aplica a várias classes de mensagens, não apenas a convention
s.)
Se eu tiver aulas como
class related_methods():
def a_method(self):
self.stack.function(self.my_var)
class more_methods():
def b_method(self):
self.otherfunc()
class implement_methods(related_methods, more_methods):
def __init__(self):
self.stack = some()
self.my_var = other()
def otherfunc(self):
self.a_method()
Obviamente, isso é artificial. Aqui está um exemplo melhor, se você quiser .
Eu acredito que esse estilo é chamado usando "mixins".
Como outras ferramentas, pylint
classifica esse código em -21.67 / 10
, principalmente porque ele pensa more_methods
e related_methods
não tem self
ou atributos otherfunc
, stack
e my_var
porque sem executar o código, ele aparentemente não pode ver related_methods
e more_methods
está misturado implement_methods
.
Compiladores e ferramentas de análise estática nem sempre podem resolver o Problema da Parada , mas acho que esse é certamente um caso em que analisar o que é herdado por implement_methods
mostraria que isso é perfeitamente válido e que seria uma coisa muito fácil de fazer.
Por que as ferramentas de análise estática rejeitam esse padrão OOP válido (eu acho)?
Ou:
Eles nem tentam verificar a herança ou
mixins são desencorajados em Python idiomático e legível
O número 1 está obviamente incorreto, porque se eu pedir pylint
para me contar sobre uma classe minha que herda unittest.TestCase
esse uso self.assertEqual
(algo definido apenas em unittest.TestCase
), ela não reclama.
Os mixins são antitônicos ou desencorajados?
Respostas:
Os mixins simplesmente não são um caso de uso considerado pela ferramenta. Isso não significa que seja necessariamente um caso de uso ruim, apenas um caso incomum para python.
Se mixins são usados adequadamente em uma instância específica é outra questão. O anti-padrão de mixina que eu vejo com mais frequência é o uso de mixinas quando existe a intenção de haver apenas uma combinação. Essa é apenas uma maneira indireta de esconder uma classe divina. Se você não consegue pensar em um motivo agora para trocar ou deixar de fora um dos mixins, não deve ser um mixin.
fonte
Eu acredito que os Mixins podem, absolutamente, ser pitonicos. No entanto, a maneira idiomática de silenciar seu interlocutor - e melhorar a legibilidade de seus Mixins - é: (1) definir métodos abstratos que definem explicitamente os métodos que os filhos de um Mixin devem implementar, e (2) pré-definir
None
avaliados para membros de dados Mixin que os filhos devem inicializar.Aplicando esse padrão ao seu exemplo:
fonte
abstract other(self): pass
coisa troque a confusão do linter pela minha #Eu acho que mixins podem ser bons, mas também acho que o pylint está certo neste caso. Isenção de responsabilidade: material baseado em opinião a seguir.
Uma boa classe, incluindo os mixins, tem uma responsabilidade clara. Idealmente, um mixin deve conter todo o estado que ele acessará e a lógica para lidar com isso. Por exemplo, um bom mixin pode adicionar um
last_updated
campo a uma classe de modelo ORM, fornecer lógica para defini-lo e métodos para procurar o registro mais antigo / mais novo.Referir-se a membros da instância não declarados (variáveis e métodos) parece um pouco estranho.
A abordagem correta depende muito da tarefa em questão.
Pode ser uma mistura com o bit relevante de estado mantido nele.
Pode ser uma hierarquia de classe diferente, na qual os métodos que você distribui atualmente por meio de um mixin estão em uma classe base, enquanto as diferenças de implementação de nível inferior pertencem às subclasses. Isso parece mais adequado ao seu caso com as operações de pilha.
Pode ser um decorador de classe que adicione um método ou dois; isso geralmente faz sentido quando você precisa passar alguns argumentos para o decorador para afetar a geração do método.
Declare o seu problema, explique suas preocupações maiores de design e poderíamos argumentar se algo é um antipadrão no seu caso.
fonte
O linter não está ciente de que você usa uma classe como um mixin. Pylint está ciente de que você usa um mixin se adicionar o sufixo 'mixin' ou 'Mixin' no final do nome da classe, e o linter para de reclamar.
Mixins não são ruins ou bons por si só, são apenas uma ferramenta. Você faz deles um bom uso ou um mau uso.
fonte
Os mixins estão ok?
Os mixins não são desencorajados - eles são um bom caso de uso para herança múltipla.
Por que seu linter está reclamando?
Pylint é, obviamente, reclamando porque ele não sabe onde o
otherfunc
,stack
emy_var
está vindo.Trivial?
Não há motivo aparente imediatamente bom para você separar esses dois métodos em classes pai separadas, seja no seu exemplo na pergunta ou no seu exemplo vinculado mais trivial, mostrado aqui.
Custos de mixins e ruído
Os custos do que você está fazendo é fazer com que o seu relatório seja interrompido. Esse ruído pode ocultar problemas mais importantes no seu código. Este é um custo importante para pesar. Outro custo é que você está separando seu código inter-relacionado em diferentes namespaces, o que pode dificultar a descoberta do significado pelos programadores.
Conclusão
A herança permite a reutilização de código. Se você conseguir reutilizar o código com seus mixins, ótimo, eles criaram valor para você que provavelmente supera os outros custos possíveis. Se você não está recebendo reutilização / deduplicação de linhas de código, provavelmente não está obtendo muito valor para seus mixins e, nesse ponto, acho que o custo do ruído é maior do que os benefícios.
fonte