Soy plenamente consciente de que pylint
otras herramientas de análisis estático no lo saben todo y, a veces, sus consejos deben ser desobedecidos. (Esto se aplica a varias clases de mensajes, no solo convention
s.)
Si tengo clases 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, eso es artificial. Aquí hay un mejor ejemplo, si lo desea .
Creo que este estilo se llama usando "mixins".
Al igual que otras herramientas, pylint
califica este código -21.67 / 10
, principalmente porque piensa more_methods
y related_methods
no tiene self
o atributos otherfunc
, stack
y my_var
porque sin ejecutar el código, aparentemente no puede ver related_methods
y more_methods
está mezclado implement_methods
.
Los compiladores y las herramientas de análisis estático no siempre pueden resolver el problema de detención , pero creo que este es ciertamente un caso en el que ver lo que hereda implement_methods
demostraría que esto es perfectamente válido, y eso sería algo muy fácil de hacer.
¿Por qué las herramientas de análisis estático rechazan este patrón OOP válido (creo)?
Ya sea:
Ni siquiera intentan verificar la herencia o
los mixins se desaniman en Python idiomático y legible
El # 1 es obviamente incorrecto porque si le pido pylint
que me cuente sobre una clase mía que hereda los unittest.TestCase
usos self.assertEqual
, (algo definido solo en unittest.TestCase
), no se queja.
¿Son los mixins antipáticos o desanimados?
Respuestas:
Los mixins simplemente no son un caso de uso considerado por la herramienta. Eso no significa que sea necesariamente un mal caso de uso, solo uno poco común para Python.
Si los mixins se usan apropiadamente en un caso particular es otra cuestión. El anti-patrón de mixina que veo con más frecuencia es el uso de mixinas cuando solo se pretende que haya una combinación mixta. Esa es solo una forma indirecta de ocultar una clase de dios. Si no puede pensar en una razón en este momento para cambiar o dejar de lado uno de los mixins, no debería ser un mixin.
fuente
Creo que los Mixins pueden ser absolutamente pitónicos. Sin embargo, la forma idiomática de silenciar su linter, y mejorar la legibilidad de sus Mixins, es tanto (1) definir métodos abstractos que definan explícitamente los métodos que los hijos de un Mixin deben implementar, como (2) predefinir
None
-valorados para los miembros de datos Mixin que los niños deben inicializar.Aplicando este patrón a tu ejemplo:
fuente
abstract other(self): pass
cosa cambia la confusión de la pelotilla por la míaCreo que los mixins pueden ser buenos, pero también creo que pylint es correcto en este caso. Descargo de responsabilidad: sigue el material basado en la opinión.
Una buena clase, mixins incluidos, tiene una clara responsabilidad. Idealmente, un mixin debería llevar todo el estado al que accederá, y la lógica para manejarlo. Por ejemplo, un buen mixin podría agregar un
last_updated
campo a una clase de modelo ORM, proporcionar lógica para establecerlo y métodos para buscar el registro más antiguo / más nuevo.Hacer referencia a miembros de instancia no declarados (variables y métodos) parece un poco extraño.
El enfoque correcto depende mucho de la tarea en cuestión.
Puede ser una mezcla con el estado relevante que se mantiene en él.
Puede ser una jerarquía de clases diferente donde los métodos que distribuye actualmente a través de un mixin están en una clase base, mientras que las diferencias de implementación de nivel inferior pertenecen a subclases. Esto se ve más adecuado para su caso con las operaciones de pila.
Puede ser un decorador de clase que agrega uno o dos métodos; Esto generalmente tiene sentido cuando tiene que pasar algunos argumentos al decorador para afectar la generación del método.
Exponga su problema, explique sus preocupaciones de diseño más grandes, entonces podríamos discutir si algo es un antipatrón en su caso.
fuente
El linter no sabe que usa una clase como mixin. Pylint es consciente de que usa un mixin si agrega el sufijo 'mixin' o 'Mixin' al final del nombre de la clase, luego el linter deja de quejarse.
Los mixins no son malos ni buenos per se, son solo una herramienta. Les haces un buen uso o un mal uso.
fuente
¿Están bien los mixins?
Los mixins no se desaniman, son un buen caso de uso para la herencia múltiple.
¿Por qué se queja tu linter?
Pylint es, obviamente, quejándose porque no sabe dónde está el
otherfunc
,stack
ymy_var
está viniendo.¿Trivial?
No hay una razón aparente inmediata para que separe estos dos métodos en clases primarias separadas, ya sea en su ejemplo en la pregunta o en su ejemplo vinculado más trivial, que se muestra aquí.
Costos de mixins y ruido.
El costo de lo que está haciendo es hacer que su linter informe de ruido. Ese ruido puede ocultar problemas más importantes con su código. Este es un costo importante para pesar. Otro costo es que está separando su código interrelacionado en diferentes espacios de nombres que pueden dificultar que los programadores descubran el significado de.
Conclusión
La herencia permite la reutilización del código. Si obtiene la reutilización del código con sus mixins, excelente, han creado un valor para usted que probablemente supera los posibles otros costos. Si no está obteniendo la reutilización / deduplicación de líneas de código, probablemente no esté obteniendo mucho valor por sus mixins, y en ese momento, creo que el costo del ruido es mayor que los beneficios.
fuente