¿** kwargs es un antipatrón?

15

Tenemos una gran cantidad de código en nuestra base de código interna que llama a nuestras bibliotecas internamente; estas bibliotecas a menudo tienen muchos argumentos (piense en matplotlib) y nuestro código a menudo solo realiza una tarea específica y simplemente pasa **kwargsla siguiente función llamada.

P.ej:

def our_method(dataframe, **kwargs):
    result = do_something_with_data(dataframe)
    external_module.draw(result, **kwargs)

Si bien **kwargsnos impide repetir todos los parámetros en nuestra declaración de método, también hace que sea extremadamente opaco qué argumentos son válidos al llamar our_method: tengo que saber qué método se llama, lo que a menudo no quiero saber.

¿Cuál es su opinión sobre esto?

Christian Sauer
fuente

Respuestas:

15

¿Cómo usan los desarrolladores su código? En otras palabras, ¿qué hacen exactamente para determinar qué argumentos deben usarse y cómo?

  • Si confían en la documentación generada automáticamente a partir de su código, y el generador no tiene idea de qué hacer **kwargs, esto es realmente problemático. En lugar de encontrar la lista de argumentos y su significado en la documentación, no tienen absolutamente ninguna información, excepto el vago "toma algunos argumentos".

    Este problema probablemente se resuelva documentando el método manualmente, reemplazando la documentación generada automáticamente. Esto requiere un trabajo adicional del implementador del método, pero recuerde que el código (y su documentación) se lee con mucha más frecuencia de lo que se escribe.

  • Si el código es su documentación, los desarrolladores que usan el método **kwargsnecesitan dos pasos adicionales: necesitan no solo mirar la firma del método, sino también su implementación real, para encontrar el otro método que realmente llama. Luego, necesitan ir a este otro método para finalmente encontrar lo que estaban buscando.

    Esto no implica mucho esfuerzo, pero aún así, el esfuerzo debe repetirse una y otra vez. La peor parte es que no puede ayudarlos agregando documentación: si comenta su método, enumera los argumentos reales, existe un gran riesgo de que la próxima versión de la biblioteca a la que llama su método tenga diferentes argumentos, y su documentación tendrá estar desactualizado, ya que nadie recordará que debe mantenerse actualizado.

Mi recomendación es confiar **kwargssolo en los métodos que tienen un alcance reducido. Los métodos privados (y por privado en un contexto de Python, me refiero a los métodos que comienzan por _) que se utilizan en pocos lugares de la clase son buenos candidatos, por ejemplo. Por otro lado, los métodos que utilizan docenas de clases en todo el código base son muy malos candidatos.

Después de todo, no debería tomar demasiado esfuerzo reescribir los argumentos de un método que llame dentro del método que escribe. Con suerte, la mayoría de los métodos no requieren más de seis u ocho argumentos, y si lo hacen, pregúntese si no debería refactorizar el código. En todos los casos:

  • Hacer argumentos explícitos dentro de su método no requiere mucho esfuerzo,

  • Es posible que luego desee validar los argumentos de todos modos (aunque si confía solo en este punto para hacer que los argumentos sean explícitos, viola YAGNI).

Arseni Mourzenko
fuente
Realmente me gusta esta respuesta y creo que esta es buena. Desafortunadamente, gran parte de nuestro código tiene muchos métodos públicos que utilizan este patrón. Pero ahora tengo argumentos de que deberíamos cambiarlo (y descartar matplotlib, nunca vi una "interfaz" más horrible ...)
Christian Sauer
3

Si la función de siguiente nivel tiene un __doc__, puede copiar el __doc__ a su nueva función.

Por ejemplo:

def a(x):
    """This function takes one parameter, x, and does nothing with it!"""
    pass

def b(**kwargs):
    a(**kwargs)

b.__doc__=a.__doc__

Esto podría aplicarse de forma recursiva, y podría ser aplicado por un decorador (lo que podría ser útil si está haciendo esto de forma masiva de todos modos). La cadena __doc__ también podría manipularse para agregar más al final. Esto significa que los parámetros mostrados seguirían siendo kwargs, pero al menos hay documentación en la ayuda que describe los parámetros reales.

AMADANON Inc.
fuente