Programación limpia al escribir código científico

169

Realmente no escribo proyectos grandes. No mantengo una gran base de datos ni trato con millones de líneas de código.

Mi código es principalmente material de tipo "script" (cosas para probar funciones matemáticas o para simular algo), "programación científica". Los programas más largos en los que he trabajado hasta ahora son un par de cientos de líneas de código, y la mayoría de los programas en los que trabajo son alrededor de 150.

Mi código también es basura. Me di cuenta de esto el otro día cuando estaba tratando de encontrar un archivo que escribí hace un tiempo, pero que probablemente sobrescribí y que no uso el control de versiones, lo que probablemente hace que una gran cantidad de ustedes se encojan de agonía ante mi estupidez.

El estilo de mi código es complicado y está lleno de comentarios obsoletos que indican formas alternativas de hacer algo o con líneas de código copiadas. Si bien los nombres de las variables siempre comienzan muy bien y son descriptivos, a medida que agrego o cambio cosas según, por ejemplo, algo nuevo que alguien quiere probar, el código se superpone en la parte superior y se sobrescribe y porque siento que esto debería probarse rápidamente ahora que tengo un marco de trabajo, empiezo a usar nombres de variables malísimos y el archivo va al bote.

En el proyecto en el que estoy trabajando ahora, estoy en la fase en la que todo esto vuelve para morderme a lo grande. Pero el problema es (aparte de usar el control de versiones y hacer un nuevo archivo para cada nueva iteración y grabarlo todo en un archivo de texto en algún lugar, lo que probablemente ayudará a la situación dramáticamente) Realmente no sé cómo proceder para mejorar Mi estilo de codificación real.

¿Son necesarias las pruebas unitarias para escribir piezas de código más pequeñas? ¿Qué tal OOP? ¿Qué tipo de enfoques son buenos para escribir un código bueno y limpio rápidamente al hacer "programación científica" en lugar de trabajar en proyectos más grandes?

Hago estas preguntas porque a menudo, la programación en sí misma no es súper compleja. Se trata más de matemáticas o ciencias que estoy probando o investigando con la programación. Por ejemplo, ¿es necesaria una clase cuando dos variables y una función probablemente podrían ocuparse de ella? (Tenga en cuenta que, en general, también se trata de situaciones en las que se prefiere que la velocidad del programa sea más rápida: cuando ejecuta más de 25,000,000 pasos de una simulación, quiere que sea así).

Quizás esto es demasiado amplio, y si es así, me disculpo, pero al mirar los libros de programación, a menudo parecen abordarse en proyectos más grandes. Mi código no necesita OOP, y ya es bastante corto, así que no es como "¡oh, pero el archivo se reducirá en mil líneas si lo hacemos!" Quiero saber cómo "comenzar de nuevo" y programar limpiamente en estos proyectos más pequeños y rápidos.

Me complacería proporcionar detalles más específicos, pero cuanto más general sea el consejo, más útil, creo. Estoy programando en Python 3.


Alguien sugirió un duplicado. Permítanme aclarar que no estoy hablando de ignorar directamente los estándares de programación estándar. Claramente, hay una razón por la que existen esos estándares. Pero, por otro lado, ¿tiene sentido escribir código que diga OOP cuando algunas cosas estándar podrían haberse hecho, hubieran sido mucho más rápidas de escribir y hubieran tenido un nivel similar de legibilidad debido a la brevedad del ¿programa?

Hay excepciones Además, probablemente haya estándares para la programación científica más allá de los estándares simples. También estoy preguntando por eso. No se trata de si los estándares de codificación normales deben ignorarse al escribir código científico, ¡se trata de escribir un código científico limpio!


Actualizar

Solo pensé que agregaría una especie de actualización de "no una semana después". Todos sus consejos fueron extremadamente útiles. Ahora estoy usando el control de versiones - git, con git kraken para una interfaz gráfica. Es muy fácil de usar y ha limpiado mis archivos drásticamente, ya no es necesario que los archivos antiguos se queden pegados, o que las versiones antiguas de código hayan sido comentadas "por si acaso".

También instalé pylint y lo ejecuté en todo mi código. Un archivo obtuvo un puntaje negativo inicialmente; Ni siquiera estoy seguro de cómo fue posible. Mi archivo principal comenzó con una puntuación de ~ 1.83 / 10 y ahora está en ~ 9.1 / 10. Todo el código ahora se ajusta bastante bien a los estándares. También lo revisé con mis propios ojos actualizando nombres de variables que habían salido ... uhm ... mal, y buscando secciones para refactorizar.

En particular, hice una pregunta reciente en este sitio sobre la refactorización de una de mis funciones principales, y ahora es mucho más limpia y mucho más corta: en lugar de una función larga, hinchada, si / si no llena, ahora es menos de la mitad el tamaño y mucho más fácil descubrir qué está pasando.

Mi próximo paso es implementar "pruebas unitarias" de algún tipo. Con lo cual me refiero a un archivo que puedo ejecutar en mi archivo principal que analiza todas las funciones en él con declaraciones de aserción y try / exceptts, que probablemente no sea la mejor manera de hacerlo, y da como resultado una gran cantidad de código duplicado, pero seguiré leyendo e intentaré descubrir cómo hacerlo mejor.

También actualicé significativamente la documentación que ya había escrito y agregué archivos suplementarios como una hoja de cálculo de Excel, la documentación y un documento asociado al repositorio de github. Parece un proyecto de programación real ahora.

Entonces ... supongo que esto es todo para decir: gracias .

brezo
fuente
8
Si desea mejorar activamente su código, puede considerar publicar algunos en Revisión de código . La comunidad allí con gusto lo ayudará con eso.
hoffmale
77
Cuando dice "aparte de usar el control de versiones, y hacer un nuevo archivo para cada nueva iteración y grabarlo todo en un archivo de texto en alguna parte" por "y" quiere decir "o" porque si está usando el control de versiones no debería No se copiarán pegando versiones. El punto es que el control de versiones mantiene toda la versión anterior para usted
Richard Tingle
2
@mathreadler No creo que entiendas del todo. Sí, solo que probablemente voy a leer y jugar con el código (aunque nunca se sabe, y tengo a alguien con quien estoy trabajando que puede programar, aunque en un idioma diferente) ... pero el código todavía está mierda. Tendré que leerlo más tarde y descubrir de nuevo qué diablos estoy haciendo. Que es un problema, y puedo dar fe de que debido a que estoy experimentando ahora los efectos, y las cosas han vuelto más fácil que he implementado el control de versiones y otras técnicas sugeridas aquí.
heather

Respuestas:

163

Este es un problema bastante común para los científicos. Lo he visto mucho y siempre se debe al hecho de que la programación es algo que eliges como herramienta para hacer tu trabajo.

Entonces tus guiones son un desastre. Voy a ir en contra del sentido común y decir que, suponiendo que esté programando solo, ¡ esto no es tan malo! Nunca volverás a tocar la mayor parte de lo que escribes, así que pasar demasiado tiempo para escribir un código bonito en lugar de producir "valor" (por lo que el resultado de tu script) no te va a hacer mucho.

Sin embargo, habrá un momento en el que deberá volver a algo que hizo y ver exactamente cómo funcionaba algo. Además, si otros científicos necesitan revisar su código, es realmente importante que sea lo más claro y conciso posible, para que todos puedan entenderlo.

Su principal problema será la legibilidad, así que aquí hay algunos consejos para mejorar:

Nombres de variables:

A los científicos les encanta usar anotaciones concisas. Todas las ecuaciones matemáticas usualmente usan letras simples como variables, y no me sorprendería ver muchas y muy pequeñas variables en su código. Esto perjudica mucho la legibilidad. Cuando vuelva a su código, no recordará qué representan esos y, i y x2, y pasará mucho tiempo tratando de resolverlo. Intente nombrar sus variables explícitamente, usando nombres que representen exactamente lo que son.

Divide tu código en funciones:

Ahora que renombró todas sus variables, sus ecuaciones se ven terribles y tienen varias líneas de largo.

En lugar de dejarlo en su programa principal, mueva esa ecuación a una función diferente y asígnele un nombre. Ahora, en lugar de tener una línea de código enorme y desordenada, tendrá unas breves instrucciones que le indicarán exactamente qué está sucediendo y qué ecuación utilizó. Esto mejora tanto su programa principal, ya que ni siquiera tiene que mirar la ecuación real para saber lo que hizo, y el código de la ecuación en sí, ya que en una función separada puede nombrar sus variables como desee, y volver a Las letras individuales más familiares.

En esta línea de pensamiento, intente encontrar todas las piezas de código que representan algo, especialmente si ese algo es algo que tiene que hacer varias veces en su código, y divídalas en funciones. Descubrirá que su código será más fácil de leer rápidamente y que podrá usar las mismas funciones sin escribir más código.

En la torta, si esas funciones son necesarias en más de sus programas, simplemente puede hacer una biblioteca para ellas y las tendrá siempre disponibles.

Variables globales:

Cuando era principiante, pensaba que esta era una excelente manera de transmitir los datos que necesitaba en muchos puntos de mi programa. Resulta que hay muchas otras formas de pasar cosas, y las únicas cosas que hacen las variables globales es causar dolor de cabeza a las personas, ya que si vas a un punto aleatorio de tu programa nunca sabrás cuándo se usó o editó ese valor por última vez, rastrearlo será un dolor. Intenta evitarlos siempre que sea posible.

Si sus funciones necesitan devolver o modificar múltiples valores, haga una clase con esos valores y páselos como un parámetro, o haga que la función devuelva múltiples valores (con tuplas con nombre) y asigne esos valores en el código de la persona que llama.

Control de versiones

Esto no mejora directamente la legibilidad, pero le ayuda a hacer todo lo anterior. Cada vez que realice algunos cambios, comprométase con el control de versiones (un repositorio local de Git estará bien), y si algo no funciona, ¡mire lo que cambió o simplemente retroceda! Esto facilitará la refactorización de su código, y será una red de seguridad si accidentalmente rompe cosas.

Tener todo esto en mente le permitirá escribir código claro y más efectivo, y también lo ayudará a encontrar posibles errores más rápido, ya que no tendrá que atravesar funciones gigantescas y variables desordenadas.

BgrWorker
fuente
56
Excelente consejo Sin embargo, no puedo votar por el comentario "eso no es tan malo". Es muy malo. Los guiones científicos de baja calidad son un gran problema para la reproducibilidad en el análisis de datos y una fuente frecuente de error en el análisis. Escribir un buen código es valioso no solo para que pueda entenderlo más tarde, sino también para evitar errores en primer lugar.
Jack Aidley
22
Si el código es una implementación de una fórmula bien conocida, entonces los nombres de variables de una sola letra y tal podrían ser lo correcto. Depende de la audiencia ... y más al punto, si se espera que el lector ya sepa lo que significan los nombres.
cHao
11
@cHao el problema no es cuando esas variables están conectadas en la fórmula (de ahí el consejo de "renombrarlas dentro de la función"), sino cuando se leen y manipulan fuera de ella, y cuando comienzan a entrar en conflicto con otras variables en el futuro (por ejemplo, he visto a personas que necesitan tres variables "x" nombrarlas x1, x2, x3)
BgrWorker
44
"Voy a ir en contra del sentido común ..." No, no lo eres. Vas en contra del dogma prevaleciente, que es en sí mismo contra el sentido común. ;) Todo esto es un buen consejo.
jpmc26
3
Como programador científico (anterior), generalmente adopto una regla de tres. Si termino escribiendo código similar tres veces, la funcionalidad se desarrolla y se escribe en un módulo separado con documentación (a menudo solo comentarios, pero eso es suficiente). Esto limita el caos de la programación ad-hoc y me permite construir una biblioteca que pueda ampliar en el futuro.
rcollyer
141

Físico aquí. Estado allí.

Yo diría que su problema no se trata de la elección de herramientas o paradigmas de programación (pruebas unitarias, OOP, lo que sea). Se trata de la actitud , la mentalidad. El hecho de que sus nombres de variables estén bien elegidos al principio y terminen siendo basura es bastante revelador. Si piensa que su código es "ejecutar una vez, luego tirar", inevitablemente será un desastre. Si lo piensas como el producto de la artesanía y el amor, será hermoso.

Creo que solo hay una receta para escribir código limpio: escríbelo para el ser humano que lo va a leer, no para el intérprete que lo va a ejecutar. Al intérprete no le importa si su código es un desastre, pero al lector humano sí le importa.

Usted es un científico Probablemente puedas pasar mucho tiempo puliendo un artículo científico. Si su primer borrador parece complicado, lo refactorizará hasta que la lógica fluya de la manera más natural. Desea que sus colegas lo lean y encuentren los argumentos claros como el cristal. Desea que sus alumnos puedan aprender de él.

Escribir código limpio es exactamente lo mismo. Piense en su código como una explicación detallada de un algoritmo que solo por casualidad es legible por máquina. Imagina que vas a publicarlo como un artículo que la gente leerá. Incluso lo va a mostrar en una conferencia y guiará a la audiencia línea por línea. Ahora ensaya tu presentación . ¡Sí, línea por línea ! Vergonzoso, ¿no? Así que limpia tus diapositivas (err ... quiero decir, tu código), y ensaya nuevamente. Repita hasta que esté satisfecho con el resultado.

Sería aún mejor si, después de los ensayos, puedes mostrar tu código a personas reales en lugar de solo a personas imaginarias y a ti mismo en el futuro. Pasarlo línea por línea se llama "caminata de código", y no es una práctica tonta.

Por supuesto, todo esto tiene un costo. Escribir código limpio lleva mucho más tiempo que escribir código desechable. Solo usted puede evaluar si los beneficios superan el costo para su caso de uso particular.

En cuanto a las herramientas, he dicho antes que no son que importante. Sin embargo, si tuviera que elegir uno, diría que el control de versiones es el más útil.

Edgar Bonet
fuente
32
«Escribir código limpio es exactamente lo mismo [que escribir un artículo claro].» Estoy totalmente de acuerdo con eso, ¡bueno!
juandesant
43
Esto es algo que la mayoría de los programadores profesionales olvidan decir porque es muy obvio. Su código está terminado cuando otro programador pueda leerlo y modificarlo. No cuando se ejecuta y produce la salida correcta. El OP necesita pasar una hora extra por script refactorizando y comentando su código para que sea legible para los humanos.
UEFI
31
Aunque escribir código limpio lleva mucho más tiempo que escribir código desechable, mucho más importante es que leer código desechable lleva mucho más tiempo que leer código limpio.
user949300
3
@UEFI No, es algo que la mayoría de los programadores profesionales ni siquiera se dan cuenta. O no te importa.
jpmc26
2
De acuerdo al 100%. El estadístico se convirtió en programador, por lo que hago una buena cantidad de programación 'científica' en el trabajo. El código claro, con comentarios significativos es un salvavidas cuando tiene que volver a ese código 1, 4 o 12 meses después. Leer el código te dice qué está haciendo el código. Leer los comentarios te dice qué se supone que debe hacer el código.
railsdog
82

El control de versiones probablemente te dará más por tu dinero. No es solo para el almacenamiento a largo plazo, es ideal para realizar un seguimiento de su experimentación a corto plazo y volver a la última versión que funcionó, manteniendo notas en el camino.

Los siguientes más útiles son las pruebas unitarias. Lo que pasa con las pruebas unitarias es que incluso las bases de código con millones de líneas de código se prueban unitariamente una función a la vez. Las pruebas unitarias se realizan en el nivel más pequeño de abstracción. Eso significa que básicamente no hay diferencia entre las pruebas unitarias escritas para bases de código pequeñas y las utilizadas para las grandes. Solo hay más de ellos.

Las pruebas unitarias son la mejor manera de evitar romper algo que ya estaba funcionando cuando arreglas algo más, o al menos decirte rápidamente cuándo lo haces. En realidad, son más útiles cuando no es tan hábil como programador, o no sabe cómo o no desea escribir un código más detallado que esté estructurado para hacer que los errores sean menos probables o más obvios.

Entre el control de versiones y las pruebas de unidades de escritura a medida que avanza, su código naturalmente se volverá mucho más limpio. Se pueden aprender otras técnicas para una codificación más limpia cuando se alcanza una meseta.

Karl Bielefeldt
fuente
78
Religiosamente pruebo la mayor parte de mi código, pero he encontrado que las pruebas unitarias exploratorias, el código científico son menos que inútiles . La metodología fundamentalmente no parece funcionar aquí. No conozco a ningún científico computacional en mi campo que pruebe unitariamente su código de análisis. No estoy seguro de cuál es la razón de esta falta de coincidencia, pero una de las razones es sin duda que, a excepción de las unidades triviales, es difícil o imposible establecer buenos casos de prueba.
Konrad Rudolph el
22
@KonradRudolph el truco en esos casos es la separación clara de las preocupaciones entre partes de su código que tienen un comportamiento claramente definible (lea esta entrada, calcule este valor) de las partes de su código que son realmente exploratorias o se están adaptando a, por ejemplo, alguna salida o visualización legible por humanos. Es probable que el problema aquí sea que la separación deficiente de las preocupaciones lleva a difuminar esas líneas, lo que lleva a la percepción de que las pruebas unitarias en este contexto son imposibles, lo que lo lleva de regreso al inicio en un ciclo repetitivo.
Ant P
18
En una nota al margen, el control de versiones también funciona bastante bien para los documentos de LaTeX, ya que el formato es compatible con la diferencia de texto. De esta manera, puede tener un repositorio tanto para sus documentos como para el código que los admite. Sugiero buscar en el control de versiones distribuido, como Git. Hay una pequeña curva de aprendizaje, pero una vez que la entiendes, tienes una manera limpia y agradable de iterar en tu desarrollo y tienes algunas opciones interesantes para usar una plataforma como Github, que ofrece cuentas de equipo gratuitas para académicos .
Dan Bryant
12
@AntP Es posible que simplemente no haya tanto código que se pueda refactorizar significativamente en unidades comprobables bien definidas. Una gran cantidad de código científico esencialmente está grabando un montón de bibliotecas juntas. Estas bibliotecas ya estarán bien probadas y estructuradas limpiamente, lo que significa que el autor solo tiene que escribir "pegamento", y en mi experiencia, es casi imposible escribir pruebas unitarias para pegamento que no sean tautológicas.
James_pic
77
"Entre el control de versiones y las pruebas de la unidad de escritura a medida que avanza, su código se volverá mucho más limpio". Esto no es verdad Puedo dar fe de ello personalmente. Ninguna de estas herramientas le impide escribir código malo, y en particular, escribir pruebas malas encima del código malo hace que sea aún más difícil de limpiar. Las pruebas no son una bala mágica de plata, y hablar como lo es es algo terrible para cualquier desarrollador que todavía esté aprendiendo (que es todo el mundo). Sin embargo, el control de versiones generalmente nunca causa daños al código en sí, como lo hacen las malas pruebas.
jpmc26
29

(aparte de usar el control de versiones y crear un nuevo archivo para cada nueva iteración y grabarlo todo en un archivo de texto en algún lugar, lo que probablemente ayudará a la situación dramáticamente)

Probablemente lo habría descubierto usted mismo, pero si tiene que " grabarlo todo en un archivo de texto en alguna parte ", no está utilizando el sistema de control de versiones en todo su potencial. Use algo como Subversion, git o Mercurial y escriba un buen mensaje de confirmación con cada confirmación y tendrá un registro que cumple el propósito del archivo de texto pero que no puede separarse del repositorio.

Aparte de eso, usar el control de versiones es lo más importante que puede hacer por una razón que ninguna de las respuestas existentes menciona: la reproducibilidad de los resultados . Si puede usar sus mensajes de registro o agregar una nota a los resultados con el número de revisión, entonces puede estar seguro de poder regenerar los resultados, y estará en mejores condiciones para publicar el código con el documento.

¿Son necesarias las pruebas unitarias para escribir piezas de código más pequeñas? ¿Qué tal OOP? ¿Qué tipo de enfoques son buenos para escribir un código bueno y limpio rápidamente al hacer "programación científica" en lugar de trabajar en proyectos más grandes?

La prueba de unidades nunca es necesaria, pero es útil si (a) el código es lo suficientemente modular como para que pueda probar unidades en lugar de todo; (b) puedes crear pruebas. Idealmente, podría escribir la salida esperada a mano en lugar de generarla con el código, aunque generarla por código al menos puede proporcionarle pruebas de regresión que le indican si algo cambió su comportamiento. Solo considere si es más probable que las pruebas tengan errores que el código que están probando.

OOP es una herramienta. Úselo si ayuda, pero no es el único paradigma. Supongo que solo conoce realmente la programación de procedimientos: si ese es el caso, entonces, en el contexto descrito, creo que se beneficiaría más de estudiar programación funcional que OOP, y en particular la disciplina de evitar los efectos secundarios cuando sea posible. Python se puede escribir en un estilo muy funcional.

Peter Taylor
fuente
44
+1 para enviar mensajes; son como comentarios que no pueden quedar desactualizados porque están vinculados a la versión del código cuando realmente eran aplicables. Para comprender su código anterior, es más fácil revisar el historial del proyecto (si los cambios se confirman con una granularidad razonable) que leer comentarios obsoletos.
Silly Freak
Subversion, git y Mercurial no son fungibles. Yo recomendaría usar Git (o Mercurial) con un repositorio local sobre Subversion. Con un codificador en solitario, defectos de Subversion son un problema menor, pero no es una gran herramienta para el desarrollo en colaboración y que potencialmente pueden ocurrir en la investigación
mcottle
2
@mcottle, personalmente prefiero git, pero no pensé que este fuera el lugar correcto para entrar en detalles sobre las diferencias, especialmente porque la elección es una de las guerras religiosas activas. Es mejor alentar a OP a usar algo que ahuyentarlos del área, y la decisión no es permanente en ningún caso.
Peter Taylor
21

En la escuela de posgrado, yo mismo escribí un código de algoritmo pesado. Es un poco duro de roer. Para decirlo en términos generales, muchas convenciones de programación se basan en la idea de poner información en una base de datos, recuperarla en el momento adecuado y luego masajear esos datos para presentarlos a un usuario, generalmente usando una biblioteca para cualquier matemática partes pesadas del algoritmo de ese proceso. Para estos programas, todo lo que ha escuchado acerca de OOP, dividir el código en funciones cortas y hacer que todo sea fácilmente comprensible de un vistazo cuando sea posible es un excelente consejo. Pero no funciona para el código de algoritmo pesado, o el código que implementa cálculos matemáticos complejos y poco más.

Si está escribiendo guiones para realizar cálculos científicos, probablemente tenga escritos con las ecuaciones o algoritmos que usa. Si está utilizando nuevas ideas que ha descubierto por su cuenta, es de esperar que las publique en sus propios documentos. En este caso, la regla es: desea que su código se lea lo más parecido posible a las ecuaciones publicadas. Aquí hay una respuesta sobre Ingeniería de Software. SE con más de 200 votos a favor que defienden este enfoque y explican cómo se ve: ¿Hay una excusa para nombres de variables cortas?

Como otro ejemplo, hay algunos fragmentos geniales de código en Simbody , una herramienta de simulación física utilizada para la investigación y la ingeniería física. Estos fragmentos tienen un comentario que muestra una ecuación que se utiliza para un cálculo, seguida de un código que se lee lo más cerca posible de las ecuaciones que se implementan.

ContactGeometry.cpp:

// t = (-b +/- sqrt(b^2-4ac)) / 2a
// Discriminant must be nonnegative for real surfaces
// but could be slightly negative due to numerical noise.
Real sqrtd = std::sqrt(std::max(B*B - 4*A*C, Real(0)));
Vec2 t = Vec2(sqrtd - B, -sqrtd - B) / (2*A);

ContactGeometry_Sphere.cpp:

// Solve the scalar Jacobi equation
//
//        j''(s) + K(s)*j(s) = 0 ,                                     (1)
//
// where K is the Gaussian curvature and (.)' := d(.)/ds denotes differentiation
// with respect to the arc length s. Then, j is the directional sensitivity and
// we obtain the corresponding variational vector field by multiplying b*j. For
// a sphere, K = R^(-2) and the solution of equation (1) becomes
//
//        j  = R * sin(1/R * s)                                        (2)
//          j' =     cos(1/R * s) ,                                      (3)
//
// where equation (2) is the standard solution of a non-damped oscillator. Its
// period is 2*pi*R and its amplitude is R.

// Forward directional sensitivity from P to Q
Vec2 jPQ(R*sin(k * s), cos(k * s));
geod.addDirectionalSensitivityPtoQ(jPQ);

// Backwards directional sensitivity from Q to P
Vec2 jQP(R*sin(k * (L-s)), cos(k * (L-s)));
geod.addDirectionalSensitivityQtoP(jQP);
Kevin
fuente
99
Más uno para hacer "código para leer lo más parecido posible a las ecuaciones publicadas". Lo sentimos, defensores de nombres de variables largos y significativos. Los nombres más significativos en el código científico a menudo son desagradables, cortos y brutal precisamente porque esa es exactamente la convención utilizada en un artículo de revista científica que el código está tratando de implementar. Para una gran cantidad de código de ecuaciones que implementa ecuaciones encontradas en un artículo de revista, a menudo es mejor mantenerse lo más cerca posible de la nomenclatura en el documento, y si esto va en contra de los estándares de buena codificación, difícil.
David Hammen
@DavidHammen: Como estudiante de posgrado, respeto eso. Como programador, insisto en que tengas un bloque de comentarios gigante en la parte superior de cada función que describa en inglés sencillo (o el idioma que elijas) lo que representaba cada variable, incluso si solo era un marcador de posición temporal. De esa manera, al menos tengo una referencia a la que mirar.
tonysdg
1
@DavidHammen Además, el soporte de Python para UTF-8 en archivos fuente y reglas simples para nombres de variables hace que sea fácil declarar λo en φlugar de lo feo lambda_o phy...
Mathias Ettinger
1
@tonysdg Ya tienes una referencia; se llama "Hammen, et al. (2018)" (o lo que sea). Explicará los significados de las variables con mucho más detalle que cualquier bloque de comentarios. La razón para mantener los nombres de las variables cerca de la notación en el documento es precisamente para facilitar la conexión de lo que está en el papel con lo que está en el código.
Nadie
17

Por lo tanto, mi trabajo diario es en la publicación y preservación de datos de investigación para el sistema de la Universidad de California. Un par de personas han mencionado la reproducibilidad, y creo que ese es realmente el problema principal aquí: documentar su código de la forma en que documentaría cualquier otra cosa que alguien necesita para reproducir su experimento e, idealmente, escribir código que lo haga sencillo para otra persona para reproducir su experimento y verificar sus resultados en busca de fuentes de error.

Pero algo que no he visto mencionado, que creo que es importante, es que las agencias de financiación están mirando cada vez más la publicación de software como parte de la publicación de datos, y haciendo que la publicación de software sea un requisito para la ciencia abierta.

Con ese fin, si desea algo específico, dirigido a investigadores en lugar de desarrolladores de software en general, no puedo recomendar la organización de Carpintería de software lo suficiente. Si puedes asistir a uno de sus talleres , genial; Si todo lo que tiene tiempo / acceso para hacer es leer algunos de sus documentos sobre las mejores prácticas de computación científica , eso también es bueno. De este último:

Los científicos suelen desarrollar su propio software para estos fines porque hacerlo requiere un conocimiento sustancial específico del dominio. Como resultado, estudios recientes han encontrado que los científicos generalmente pasan el 30% o más de su tiempo desarrollando software. Sin embargo, el 90% o más de ellos son principalmente autodidactas y, por lo tanto, carecen de exposición a prácticas básicas de desarrollo de software, como escribir código mantenible, usar control de versiones y rastreadores de problemas, revisiones de código, pruebas unitarias y automatización de tareas.

Creemos que el software es solo otro tipo de aparato experimental y debe construirse, verificarse y usarse con tanto cuidado como cualquier aparato físico. Sin embargo, aunque la mayoría de los científicos tienen cuidado de validar sus equipos de laboratorio y de campo, la mayoría no sabe qué tan confiable es su software. Esto puede conducir a graves errores que afectan las conclusiones centrales de la investigación publicada. ...

Además, debido a que el software se usa a menudo para más de un solo proyecto, y otros científicos lo reutilizan, los errores informáticos pueden tener un impacto desproporcionado en el proceso científico. Este tipo de impacto en cascada causó varias retracciones importantes cuando no se descubrió un error del código de otro grupo hasta después de la publicación.

Un resumen de alto nivel de las prácticas que recomiendan:

  1. Escribir programas para personas, no para computadoras
  2. Deje que la computadora haga el trabajo
  3. Hacer cambios incrementales
  4. No te repitas a ti mismo (ni a otros)
  5. Planifica los errores
  6. Optimice el software solo después de que funcione correctamente
  7. Diseño y propósito del documento, no mecánica.
  8. Colaborar

El documento entra en detalles considerables sobre cada uno de estos puntos.

David Moles
fuente
16

¿Realmente tiene sentido escribir código que diga OOP cuando algunas cosas estándar podrían haberse hecho, hubieran sido mucho más rápidas de escribir y hubieran tenido un nivel de legibilidad similar debido a la brevedad del programa?

Respuesta personal:
también escribo muchos scripts para fines científicos. Para scripts más pequeños, simplemente trato de seguir buenas prácticas generales de programación (es decir, usar el control de versiones, practicar el autocontrol con nombres de variables). Si solo estoy escribiendo algo para abrir o visualizar rápidamente un conjunto de datos, no me molesto con OOP.

Respuesta general:
"Depende". Pero si tiene dificultades para determinar cuándo usar un concepto o paradigmas de programación, aquí hay un par de cosas en las que pensar:

  • Escalabilidad: ¿El script se mantendrá solo o eventualmente se usará en un programa más grande? Si es así, ¿la programación más grande usa OOP? ¿Se puede integrar fácilmente el código en su script en el programa más grande?
  • Modularidad: en general, su código debe ser modular. Sin embargo, OOP divide el código en fragmentos de una manera muy especial. ¿Tiene sentido ese tipo de modularidad (es decir, dividir su secuencia de comandos en clases) para lo que está haciendo?

Quiero saber cómo "comenzar de nuevo" y programar limpiamente en estos proyectos más pequeños y rápidos.

# 1: Familiarícese con lo que hay ahí fuera:
aunque solo esté "escribiendo" scripts (y realmente solo le interese el componente de ciencia), debería tomarse un tiempo para aprender sobre diferentes conceptos y paradigmas de programación. De esa manera, puede tener una mejor idea de lo que debe / no debe usar y cuándo. Eso puede sonar un poco desalentador. Y aún puede tener la pregunta: "¿Dónde empiezo / qué empiezo a mirar?" Intento explicar un buen punto de partida en los próximos dos puntos.

# 2: Comience a arreglar lo que sabe que está mal:
Personalmente, comenzaría con las cosas que sé que están mal. Obtenga un poco de control de versiones y comience a disciplinarse para mejorar con esos nombres de variables (es una lucha seria). Arreglar lo que sabes que está mal puede sonar obvio. Sin embargo, en mi experiencia, descubrí que arreglar una cosa me lleva a otra, y así sucesivamente. Antes de darme cuenta, descubrí 10 cosas diferentes que estaba haciendo mal y descubrí cómo solucionarlos o cómo implementarlos de manera limpia.

# 3: Obtenga un socio de programación:
si "comenzar de nuevo" para usted no implica tomar clases formales, considere asociarse con un desarrollador y pedirles que revisen su código. Incluso si no entienden la parte científica de lo que estás haciendo, podrían decirte lo que podrías haber hecho para que tu código sea más elegante.

# 4: Busque consorcios:
no sé en qué área científica se encuentra. Pero dependiendo de lo que haga en el mundo científico, intente buscar consorcios, grupos de trabajo o participantes en la conferencia. Luego vea si hay algún estándar en el que estén trabajando. Eso puede llevarlo a algunos estándares de codificación. Por ejemplo, hago mucho trabajo geoespacial. Mirar los documentos de la conferencia y los grupos de trabajo me llevaron al Consorcio Geoespacial Abierto . Una de las cosas que hacen es trabajar en estándares para el desarrollo geoespacial.

¡Espero que eso ayude!


Nota al margen: Sé que acabas de usar OOP como ejemplo. No quería que pensaras que me quedé atascado en cómo manejar la escritura de código usando OOP. Era más fácil escribir una respuesta continuando con ese ejemplo.

JustBlossom
fuente
Creo que el n. ° 3 es el tema más importante: un programador experimentado puede decirle al OP los conceptos que necesita (n. ° 1), cómo organizar los scripts de una mejor manera y cómo usar el control de versiones (n. ° 2).
Doc Brown
16

Recomiendo seguir el principio de Unix: ¡ Mantenlo simple, estúpido! (BESO)

O, dicho de otra manera: haz una cosa a la vez y hazlo bien.

Qué significa eso? Bueno, antes que nada, significa que tus funciones deberían ser cortas. Cualquier función que no pueda entenderse completamente en propósito, uso e implementación dentro de unos segundos es definitivamente demasiado larga. Es probable que haga varias cosas a la vez, cada una de las cuales debería ser una función propia. Así que divídelo.

En términos de líneas de código, mi heurística es que 10 líneas son una buena función, y cualquier cosa más allá de 20 es muy probable. Otras personas tienen otras heurísticas. La parte importante es mantener la longitud a algo que realmente pueda comprender en un instante.

¿Cómo se divide una función larga? Bueno, primero buscas patrones repetidos de código. Luego factoriza estos patrones de código, les da un nombre descriptivo y observa cómo se reduce su código . Realmente, la mejor refactorización es la refactorización que reduce el tamaño del código.

Esto es especialmente cierto cuando la función en cuestión se ha programado con copiar y pegar. Cada vez que veas un patrón tan repetido, sabrás instantáneamente que esto probablemente debería convertirse en una función propia. Este es el principio de No te repitas (DRY) . Cada vez que presionas copiar y pegar, estás haciendo algo mal. Crea una función en su lugar.

Anécdota Una
vez pasé varios meses refactorizando código que tenía funciones de aproximadamente 500 líneas cada uno. Después de que terminé, el código total fue aproximadamente mil líneas más corto; Había producido resultados negativos en términos de líneas de código. Le debía a la empresa ( http://www.geekherocomic.com/2008/10/09/programmers-salary-policy/index.html ). Aún así, creo firmemente que este fue uno de mis trabajos más valiosos que hice ...

Algunas funciones pueden ser largas porque están haciendo varias cosas distintas una tras otra. Estas no son violaciones SECAS, pero también se pueden dividir. El resultado es con frecuencia una función de alto nivel que llama a un puñado de funciones que implementan los pasos individuales de las funciones originales. Esto generalmente aumentará el tamaño del código, pero los nombres de las funciones agregadas hacen maravillas para hacer que el código sea más legible. Porque ahora tiene una función de nivel superior con todos sus pasos nombrados explícitamente. Además, después de esta división, está claro qué paso opera en qué datos. (Argumentos de función. No utiliza variables globales, ¿verdad?)

Una buena heurística para este tipo de división de funciones seccionales es cuando se siente tentado a escribir un comentario de sección, o cuando encuentra un comentario de sección en su código. Es muy probable que este sea uno de los puntos donde su función debe dividirse. El comentario de la sección también puede servir para inspirar un nombre para la nueva función.

Los principios KISS y DRY pueden llevarte lejos. No necesita comenzar con OOP, etc. inmediatamente, a menudo puede lograr grandes simplificaciones simplemente aplicando estos dos. Sin embargo, a la larga vale la pena saber acerca de OOP y otros paradigmas porque le brindan herramientas adicionales que puede usar para aclarar el código de su programa.

Finalmente, registra cada acción con un commit. Factorizas algo en una nueva función, eso es un commit . Fusionas dos funciones en una, porque realmente hacen lo mismo, eso es un commit . Si cambia el nombre de una variable, es una confirmación . Comprometerse con frecuencia. Si pasa un día y no te comprometiste, es probable que hayas hecho algo mal.

cmaster
fuente
2
Grandes puntos sobre la división de métodos largos. Otra buena heurística con respecto al primer párrafo después de la anécdota: si su método se puede dividir lógicamente en secciones y está tentado a escribir un comentario explicando lo que hace cada sección, entonces debe dividirse en los comentarios. Buenas noticias, esos comentarios probablemente le den una buena idea de cómo llamar a los nuevos métodos.
Jaquez
@Jaquez Ah, se olvidó por completo de eso. Gracias por recordarme. He actualizado mi respuesta para incluir esto :-)
cmaster
1
Grandes puntos, me gustaría simplificar esto para decir que "SECO" es el factor individual más importante. Identificar "repeticiones" y eliminarlas es la piedra angular de casi todas las otras construcciones de programación. Para decirlo de otra manera, todas las construcciones de programación están ahí, al menos en parte, para ayudarlo a crear código DRY. Comience diciendo "No Duplication Ever" y luego practique identificarlo y eliminarlo. Sea muy abierto sobre lo que podría ser un duplicado, incluso si no es un código similar, podría estar duplicando la funcionalidad ...
Bill K
11

Estoy de acuerdo con los demás en que el control de versiones resolverá muchos de sus problemas de inmediato. Específicamente:

  • No es necesario mantener una lista de los cambios que se han realizado, o tener muchas copias de un archivo, etc., ya que de eso se encarga el control de versiones.
  • No más archivos perdidos debido a sobrescrituras, etc. (siempre y cuando se limite a lo básico; por ejemplo, evite "reescribir el historial")
  • No es necesario mantener comentarios obsoletos, código muerto, etc. "por si acaso"; Una vez que estén comprometidos con el control de versiones, siéntanse libres de atacarlos. ¡Esto puede sentirse muy liberador!

Yo diría que no lo pienses demasiado: solo usa git. Apéguese a comandos simples (por ejemplo, solo una masterrama), quizás use una GUI, y debería estar bien. Como beneficio adicional, puede usar gitlab, github, etc. para publicaciones y copias de seguridad gratuitas;)

La razón por la que escribí esta respuesta fue para abordar dos cosas que podrías probar que no he visto mencionadas anteriormente. El primero es usar aserciones como una alternativa ligera a las pruebas unitarias. Las pruebas unitarias tienden a ubicarse "fuera" de la función / módulo / lo que sea que se esté probando: generalmente envían algunos datos a una función, reciben un resultado y luego verifican algunas propiedades de ese resultado. Generalmente, esta es una buena idea, pero puede ser inconveniente (especialmente para el código "desechable") por algunas razones:

  • Las pruebas unitarias deben decidir qué datos darán a la función. Esos datos deben ser realistas (de lo contrario, no tiene mucho sentido probarlos), deben tener el formato correcto, etc.
  • Las pruebas unitarias deben tener "acceso" a las cosas que quieren afirmar. En particular, las pruebas unitarias no pueden verificar ninguno de los datos intermedios dentro de una función; tendríamos que separar esa función en partes más pequeñas, probar esas piezas y conectarlas en otro lugar.
  • También se supone que las pruebas unitarias son relevantes para el programa. Por ejemplo, los conjuntos de pruebas pueden volverse "obsoletos" si ha habido grandes cambios desde la última vez que se ejecutaron, e incluso podría haber un montón de pruebas para el código que ya ni siquiera se usa.

Las afirmaciones no tienen estos inconvenientes, ya que se verifican durante la ejecución normal de un programa. En particular:

  • Como se ejecutan como parte de la ejecución normal del programa, tenemos datos reales del mundo real para jugar. Esto no requiere curación por separado y (por definición) es realista y tiene el formato correcto.
  • Las afirmaciones se pueden escribir en cualquier parte del código, por lo que podemos colocarlas donde tengamos acceso a los datos que queremos verificar. Si queremos probar algún valor intermedio en una función, ¡podemos poner algunas afirmaciones en el medio de esa función!
  • Como están escritas en línea, las aserciones no pueden "desincronizarse" con la estructura del código. Si nos aseguramos de que las aserciones estén marcadas por defecto, ¡tampoco debemos preocuparnos de que se pongan "obsoletas" ya que veremos de inmediato si pasan o no la próxima vez que ejecutemos el programa!

Usted menciona la velocidad como un factor, en cuyo caso la comprobación de aserciones puede ser indeseable en ese ciclo (pero aún así es útil para verificar la configuración y el procesamiento posterior). Sin embargo, casi todas las implementaciones de afirmaciones proporcionan una forma de desactivarlas; por ejemplo, en Python , aparentemente se pueden deshabilitar ejecutando la -Oopción (no lo sabía, ya que nunca antes había sentido la necesidad de deshabilitar ninguna de mis afirmaciones). Yo recomendaría que los dejes enpor defecto; Si su ciclo de codificación / depuración / prueba se ralentiza, es mejor que pruebe con un subconjunto más pequeño de sus datos, o realice menos iteraciones de alguna simulación durante la prueba, o lo que sea. Si termina deshabilitando aserciones en ejecuciones que no son de prueba por razones de rendimiento, lo primero que le recomiendo que haga es medir si en realidad son la fuente de la desaceleración. (Es muy fácil engañarse a nosotros mismos cuando se trata de cuellos de botella de rendimiento)

Mi último consejo sería utilizar un sistema de compilación que gestione sus dependencias. Personalmente uso Nix para esto, pero también he escuchado cosas buenas sobre Guix . También hay alternativas como Docker, que son mucho menos útiles desde una perspectiva científica pero quizás un poco más familiares.

Los sistemas como Nix se están volviendo recientemente (un poco) populares, y algunos podrían considerarlos excesivos para el código "desechable" como usted describe, pero su beneficio para la reproducibilidad de la computación científica es enorme. Considere un script de shell para ejecutar un experimento, como este (por ejemplo run.sh):

#!/usr/bin/env bash
set -e
make all
./analyse < ./dataset > output.csv

En cambio, podemos reescribirlo en una "derivación" de Nix, como esta (por ejemplo run.nix):

with import <nixpkgs> {};
runCommand "output.csv" {} ''
  cp -a ${./.} src
  cd src
  make all
  ./analyse < ./dataset > $out
''

El elemento intermedio ''...''es el código bash, el mismo que teníamos antes, excepto que ${...}puede usarse para "empalmar" en el contenido de otras cadenas (en este caso ./., que se expandirá a la ruta del directorio que contiene run.nix). La with import ...línea importa la biblioteca estándar de Nix , que permite runCommandejecutar código bash. Podemos ejecutar nuestro experimento usando nix-build run.nix, lo que dará un camino como /nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csv.

Entonces, ¿qué nos compra esto? Nix configurará automáticamente un entorno "limpio", que solo tiene acceso a las cosas que pedimos explícitamente. En particular, no tiene acceso a variables como $HOMEo ninguno de los software del sistema que hemos instalado. Esto hace que el resultado sea independiente de los detalles de nuestra máquina actual, como el contenido ~/.configo las versiones de los programas que tenemos instalados; ¡También conocido como el material que impide que otras personas reproduzcan nuestros resultados! Por eso agregué quecpcomando, ya que el proyecto no será accesible por defecto. Puede parecer molesto que el software del sistema no esté disponible para un script Nix, pero también es al revés: no necesitamos nada instalado en nuestro sistema (que no sea Nix) para usarlo en un script; solo lo pedimos y Nix se apagará y buscará / compilará / lo que sea necesario (la mayoría de las cosas se descargarán como archivos binarios; ¡la biblioteca estándar también es enorme!). Por ejemplo, si queremos un montón de paquetes particulares de Python y Haskell, para algunas versiones particulares de esos idiomas, además de algunos otros archivos basura (porque ¿por qué no?):

with import <nixpkgs> {};
runCommand "output.csv"
  {
    buildInputs = [
      gcc49 libjson zlib
      haskell.packages.ghc802.pandoc
      (python34.withPackages (pyPkgs: [
        pyPkgs.beautifulsoup4 pyPkgs.numpy pyPkgs.scipy
        pyPkgs.tensorflowWithoutCuda
      ]))
    ];
  }
  ''
    cp -a ${./.} src
    cd src
    make all
    ./analyse < ./dataset > $out
  ''

Lo mismo nix-build run.nixejecutará esto, recuperando todo lo que pedimos primero (y almacenando todo en caché en caso de que lo deseemos más adelante). La salida (cualquier archivo / directorio llamado $out) será almacenada por Nix, que es la ruta que escupe. Se identifica por el hash criptográfico de todas las entradas que solicitamos (contenido del script, otros paquetes, nombres, indicadores del compilador, etc.); esos otros paquetes se identifican mediante hashes de sus entradas, y así sucesivamente, de modo que tengamos una cadena completa de provincias para todo, hasta la versión de GCC que compiló la versión de GCC que compiló bash, ¡y así sucesivamente!

Con suerte, he demostrado que esto nos compra mucho para el código científico, y es bastante fácil comenzar con él. Los científicos también están empezando a tomarlo muy en serio, por ejemplo, (top hit de Google) https://dl.acm.org/citation.cfm?id=2830172, por lo que podría ser una habilidad valiosa para cultivar (al igual que la programación)

Warbo
fuente
2
Respuesta útil muy detallada: me gustan mucho las otras respuestas, pero las afirmaciones parecen un primer paso muy útil.
heather
9

Sin ir al tipo de mentalidad de control de versiones completo + embalaje + pruebas unitarias (que son buenas prácticas de programación que debería intentar lograr en algún momento), una solución intermedia que creo que encajaría es usar Jupiter Notebook . Esto parece integrarse mejor con la computación científica.

Tiene la ventaja de que puedes mezclar tus pensamientos con el código; explicando por qué un enfoque es mejor que otro y dejando el código antiguo como está en una sección ad-hoc. Además de usar las celdas correctamente, naturalmente lo llevará a fragmentar su código y organizarlo en funciones que pueden ayudarlo a comprenderlo.

Mathias Ettinger
fuente
1
Además, esto realmente ayuda con la reproducibilidad: puede ejecutar exactamente el mismo código para generar una cifra de publicación, por ejemplo, o para volver a algo que guardó hace meses quizás para incorporar comentarios de los revisores.
afaulconbridge
Para alguien que quiera leer más, esto también se conoce como programación alfabetizada.
llrs
6

Las principales respuestas ya son buenas, pero quería abordar algunas de sus preguntas directamente.

¿Son necesarias las pruebas unitarias para escribir piezas de código más pequeñas?

El tamaño del código no está directamente relacionado con la necesidad de pruebas unitarias. Está relacionado indirectamente: las pruebas unitarias son más valiosas en bases de código complejas , y las bases de código pequeñas generalmente no son tan complejas como las más grandes.

Las pruebas unitarias brillan para el código donde es fácil cometer errores, o cuando va a tener muchas implementaciones de este código. Las pruebas unitarias hacen poco para ayudarlo con el desarrollo actual , pero hacen mucho para evitar que cometa errores en el futuro que provoquen que el código existente se comporte de repente (aunque no haya tocado esa cosa).

Supongamos que tiene una aplicación donde la Biblioteca A realiza la cuadratura de los números, y la Biblioteca B aplica el teorema de Pitágoras. Obviamente, B depende de A. Necesitas arreglar algo en la biblioteca A, y digamos que introduces un error que cubica los números en lugar de cuadrarlos.

La Biblioteca B comenzará a comportarse mal de repente, posiblemente arrojando excepciones o simplemente dando un resultado incorrecto. Y cuando mira el historial de versiones de la biblioteca B, ve que no ha sido tocado. El resultado final problemático es que no tiene indicios de lo que podría estar yendo mal, y tendrá que depurar el comportamiento de B antes de darse cuenta de que el problema está en A. Eso es un esfuerzo perdido.

Ingrese las pruebas unitarias. Estas pruebas confirman que la biblioteca A está funcionando según lo previsto. Si introduce un error en la biblioteca A que hace que arroje malos resultados, sus pruebas unitarias lo detectarán. Por lo tanto, no se quedará atrapado tratando de depurar la biblioteca B.
Esto está más allá de su alcance, pero en un desarrollo de integración continua, las pruebas unitarias se ejecutan cada vez que alguien comete algún código, lo que significa que sabrá que rompió algo lo antes posible.

Especialmente para operaciones matemáticas complicadas, las pruebas unitarias pueden ser una bendición. Realiza algunos cálculos de ejemplo y luego escribe pruebas unitarias que comparan su salida calculada y su salida real (en función de los mismos parámetros de entrada).

Sin embargo, tenga en cuenta que las pruebas unitarias no lo ayudarán a crear un buen código, sino que lo mantendrán . Si generalmente escribe código una vez y nunca lo vuelve a visitar, las pruebas unitarias serán menos beneficiosas.

¿Qué tal OOP?

OOP es una forma de pensar sobre entidades distintas, por ejemplo:

Cuando un Customerquiere comprar un Product, habla con el Vendorpara recibir un Order. El Accountantentonces pagará el Vendor.

Compare esto con cómo un programador funcional piensa acerca de las cosas:

Cuando un cliente quiere purchaseProduct(), él talktoVendor()lo hará sendOrder()con él. El contador lo hará entonces payVendor().

Manzanas y naranjas. Ninguno de los dos es objetivamente mejor que el otro. Una cosa interesante a tener en cuenta es que para OOP, Vendorse menciona dos veces, pero se refiere a lo mismo. Sin embargo, para la programación funcional, talktoVendor()y payVendor()son dos cosas separadas.
Esto muestra la diferencia entre los enfoques. Si hay mucha lógica compartida específica del proveedor entre estas dos acciones, OOP ayuda a reducir la duplicación de código. Sin embargo, si no hay una lógica compartida entre los dos, fusionarlos en un solo Vendores un trabajo inútil (y, por lo tanto, la programación funcional es más eficiente).

La mayoría de las veces, los cálculos matemáticos y científicos son operaciones distintas que no se basan en la lógica / fórmulas compartidas implícitas. Debido a eso, la programación funcional se usa con más frecuencia que OOP.

¿Qué tipo de enfoques son buenos para escribir un código bueno y limpio rápidamente al hacer "programación científica" en lugar de trabajar en proyectos más grandes?

Su pregunta implica que la definición de "código bueno y limpio" cambia si está haciendo programación científica o trabajando en proyectos más grandes (supongo que se refiere a la empresa).

La definición de buen código no cambia. La necesidad de evitar la complejidad (que se puede hacer escribiendo un código limpio), sin embargo, no cambia.

El mismo argumento vuelve aquí.

  • Si nunca vuelve a visitar el código antiguo y comprende completamente la lógica sin necesidad de compartimentarlo, entonces no gaste un esfuerzo excesivo para hacer que las cosas sean mantenibles.
  • Si vuelve a visitar el código anterior, o la lógica requerida es demasiado compleja para que pueda abordarla de una vez (lo que requiere que compartimente las soluciones), entonces concéntrese en escribir un cierre limpio y reutilizable.

Hago estas preguntas porque a menudo, la programación en sí misma no es súper compleja. Se trata más de matemáticas o ciencias que estoy probando o investigando con la programación.

Obtengo la distinción que está haciendo aquí, pero cuando mira hacia atrás al código existente, está viendo tanto las matemáticas como la programación. Si cualquiera es artificial o complejo, entonces tendrá dificultades para leerlo.

Por ejemplo, ¿es necesaria una clase cuando dos variables y una función probablemente podrían ocuparse de ella?

Dejando a un lado los principios de OOP, la razón principal por la que escribo clases para albergar algunos valores de datos es porque simplifica la declaración de los parámetros del método y los valores de retorno. Por ejemplo, si tengo muchos métodos que usan una ubicación (par lat / lon), me cansaré rápidamente de tener que escribir float latitude, float longitudey preferiré escribir Location loc.

Esto se complica aún más cuando considera que los métodos generalmente devuelven un valor (a menos que existan características específicas del idioma para devolver más valores), y cosas como una ubicación querrían que devuelva dos valores (lat + lon). Esto lo incentiva a crear una Locationclase para simplificar su código.

Por ejemplo, ¿es necesaria una clase cuando dos variables y una función probablemente podrían ocuparse de ella?

Otra cosa interesante a tener en cuenta es que puede usar OOP sin mezclar valores de datos y métodos. No todos los desarrolladores están de acuerdo aquí (algunos lo llaman antipatrón), pero puede tener modelos de datos anémicos en los que tiene clases de datos separadas (campos de valores de almacenamiento) y clases lógicas (métodos de almacenamiento).
Esto es, por supuesto, en un espectro. No necesita estar completamente anémico, puede usarlo cuando lo considere apropiado.

Por ejemplo, un método que simplemente concatena el nombre y el apellido de una persona todavía se puede alojar en la Personclase en sí, porque no es realmente "lógica" sino más bien un valor calculado.

(Tenga en cuenta que, en general, también se trata de situaciones en las que se prefiere que la velocidad del programa sea más rápida: cuando ejecuta más de 25,000,000 pasos de una simulación, quiere que sea así).

Una clase siempre es tan grande como la suma de sus campos. Tomando el ejemplo de Locationnuevo, que consta de dos floatvalores, es importante notar aquí que un solo Locationobjeto ocupará tanta memoria como dos floatvalores separados .

En ese sentido, no importa si estás usando OOP o no. La huella de memoria es la misma.

El rendimiento en sí tampoco es un gran obstáculo para cruzar. La diferencia entre, por ejemplo, usar un método global o un método de clase no tiene nada que ver con el rendimiento en tiempo de ejecución, pero tiene mucho que ver con la generación de código de bytes en tiempo de compilación.

Piénselo de esta manera: si escribo mi receta de pastel en inglés o español, no cambia el hecho de que el pastel tardará 30 minutos en hornearse (= rendimiento en tiempo de ejecución). Lo único que cambia el idioma de la receta es cómo el cocinero mezcla los ingredientes (= compilando el código de bytes).

Para Python específicamente, no necesita precompilar explícitamente el código antes de llamarlo. Sin embargo, cuando no precompila, la compilación se producirá al intentar ejecutar el código. Cuando digo "tiempo de ejecución", me refiero a la ejecución en sí, no a la compilación que podría preceder a la ejecución.

Flater
fuente
6

Beneficios del código científico limpio

  • ... mirando libros de programación, a menudo parecen abordarse en proyectos más grandes.

  • ... ¿realmente tiene sentido escribir código que diga OOP cuando se podrían haber hecho algunas cosas estándar, habría sido mucho más rápido de escribir y habría tenido un nivel similar de legibilidad debido a la brevedad del programa?

Puede ser útil considerar su código desde la perspectiva de un futuro codificador.

  • ¿Por qué abrieron este archivo?
  • ¿Qué están buscando?

Por mi experiencia,

El código limpio debería facilitar la verificación de los resultados.

  • Facilite a los usuarios saber exactamente lo que necesitan hacer para ejecutar su programa.
  • Es posible que desee dividir su programa para que los algoritmos individuales se puedan comparar por separado.

  • Evite escribir funciones con efectos secundarios contraintuitivos donde una operación no relacionada hace que otra operación se comporte de manera diferente. Si no puede evitarlo, documente qué necesita su código y cómo configurarlo.

El código limpio puede servir como código de ejemplo para futuros codificadores

Los comentarios claros (incluidos los que muestran cómo se deben llamar las funciones) y las funciones bien separadas pueden marcar una gran diferencia en el tiempo que le toma a alguien que recién comienza (o en el futuro) hacer algo útil de su trabajo.

Además de esto, crear una "API" real para su algoritmo puede prepararlo mejor si decide convertir sus scripts en una biblioteca real para que otra persona la use.

Recomendaciones

"Citar" fórmulas matemáticas usando comentarios.

  • Agregue comentarios a las fórmulas matemáticas "cite", especialmente si utilizó optimizaciones (identidades trigonométricas, series de Taylor, etc.).
  • Si obtuvo la fórmula del libro, agregue un comentario que diga John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180, si encontró la fórmula en un sitio web o en un documento, cite eso también.
  • Recomiendo evitar los comentarios de "solo enlace", asegúrese de consultar el método por nombre en algún lugar para permitir que las personas lo busquen en Google, me he encontrado con algunos comentarios de "solo enlace" que redirigen a páginas internas antiguas y pueden ser muy frustrantes .
  • Puede intentar escribir la fórmula en su comentario si aún es fácil de leer en Unicode / ASCII, pero esto puede ser muy incómodo (los comentarios de código no son LaTeX).

Usa los comentarios sabiamente

Si puede mejorar la legibilidad de su código utilizando buenos nombres de variables / nombres de funciones, hágalo primero. Recuerda que los comentarios se mantendrán para siempre hasta que los elimines, así que intenta hacer comentarios que no pasen de moda.

Usar nombres de variables descriptivas

  • Las variables de una sola letra pueden ser la mejor opción si son parte de una fórmula.
  • Puede ser crucial que los futuros lectores puedan ver el código que escribió y compararlo con la ecuación que está implementando.
  • Cuando sea apropiado, considere agregarle un sufijo para describir su significado real, p. Ej. xBar_AverageVelocity
  • Como se mencionó anteriormente, recomiendo indicar claramente la fórmula / método que está utilizando por nombre en un comentario en alguna parte.

Escriba código para ejecutar su programa contra datos conocidos buenos y malos conocidos.

¿Son necesarias las pruebas unitarias para escribir piezas de código más pequeñas?

Creo que las pruebas unitarias pueden ser útiles, creo que la mejor forma de pruebas unitarias para el código científico es una serie de pruebas que se ejecutan en datos conocidos buenos y malos.

Escriba un código para ejecutar su algoritmo y verifique hasta qué punto el resultado se desvía de lo que espera. Esto lo ayudará a encontrar problemas (potencialmente muy malos y difíciles de encontrar) en los que codifica accidentalmente algo que causa un resultado falso positivo o comete un error que hace que la función siempre devuelva el mismo valor.

Tenga en cuenta que esto se puede hacer en cualquier nivel de abstracción. Por ejemplo, podría probar un algoritmo completo de coincidencia de patrones, o puede probar una función que simplemente calcule la distancia entre dos resultados en su proceso de optimización. Comience con las áreas que son más cruciales para sus resultados primero, y / o las partes del código que le preocupan más.

Facilite agregar nuevos casos de prueba, considere agregar funciones "auxiliares" y estructurar sus datos de entrada de manera efectiva. Esto puede significar posiblemente guardar datos de entrada en el archivo para que pueda volver a ejecutar fácilmente las pruebas, aunque tenga mucho cuidado para evitar falsos positivos o casos de prueba sesgados / resueltos trivialmente.

Considere usar algo como validación cruzada , consulte esta publicación sobre validación cruzada para obtener más información.

Usar control de versiones

Recomendaría usar el control de versiones y alojar su repositorio en un sitio externo. Hay sitios que alojarán repositorios de forma gratuita.

Ventajas:

  1. Proporciona una copia de seguridad en caso de que su disco duro falle
  2. Proporciona un historial que evita que se preocupe si un problema reciente surgido fue causado por un cambio accidental de un archivo, entre otros beneficios.
  3. Le permite utilizar la ramificación, que es una buena forma de trabajar en código a largo plazo / experimental sin afectar el trabajo no relacionado.

Tenga precaución al copiar / pegar código

El estilo de mi código es complicado y está lleno de comentarios obsoletos que indican formas alternativas de hacer algo o con líneas de código copiadas.

  • Copiar / pegar código puede ahorrarle tiempo, pero es una de las cosas más peligrosas que puede hacer, especialmente si es código que no escribió usted mismo (por ejemplo, si es código de un colega).

  • Tan pronto como haga que el código funcione y lo pruebe, le recomiendo que lo revise con mucho cuidado para cambiar el nombre de cualquier variable o comentar cualquier cosa que no entienda.

jrh
fuente
6

Las herramientas del comercio generalmente se inventan para resolver una necesidad. Si tiene la necesidad de usar la herramienta, si no, lo más probable es que no tenga que hacerlo.

Específicamente, los programas científicos no son el objetivo final, son los medios. Usted escribe el programa para resolver un problema que tiene ahora; no espera que otros lo utilicen (y tengan que mantenerlo) en diez años. Eso solo significa que no tiene que pensar en ninguna de las herramientas que permiten al desarrollador actual registrar el historial de otros, como el control de versiones, o capturar la funcionalidad en código como pruebas unitarias.

¿Qué te beneficiaría entonces?

  • El control de versiones es bueno porque le permite hacer copias de seguridad de su trabajo muy fácilmente. A partir de 2018, github es un lugar muy popular para hacerlo (y siempre puedes moverlo más tarde si es necesario; git es muy flexible). Un sustituto barato y simple para las copias de seguridad son los procedimientos de copia de seguridad automáticos en su sistema operativo (Time Machine para Mac, rsync para Linux, etc.). ¡Tu código debe estar en varios lugares!
  • Las pruebas unitarias son buenas porque si las escribe primero , se ve obligado a pensar en cómo verificar qué hace realmente el código, lo que le ayuda a diseñar una API más útil para su código. Esto es útil si alguna vez comienza a escribir código para reutilizarlo más tarde y lo ayuda mientras cambia un algoritmo porque sabe que funciona en estos casos.
  • Documentación. Aprenda a escribir la documentación adecuada en el lenguaje de programación que utiliza (javadoc para Java, por ejemplo). Escribe para el futuro tú. En este proceso, encontrará que los buenos nombres de variables facilitan la documentación. Iterar. Preste la misma atención a su documentación que un poeta a los poemas.
  • Usa buenas herramientas. Encuentre un IDE que lo ayude y apréndalo bien. Refactorizar como renombrar variables a un nombre mejor es mucho más fácil de esta manera.
  • Si tiene pares, considere usar la revisión por pares. Hacer que un extraño vea y entienda su código es la versión aquí y ahora del futuro para el que escribe. Si su compañero no entiende su código, probablemente tampoco lo hará más tarde.
Thorbjørn Ravn Andersen
fuente
¿Cómo no ha recibido esta respuesta un voto positivo? Tiene ahora. Nuestro grupo ha encontrado que la revisión por pares es una de las herramientas más efectivas de todas, mucho más importante que las pruebas unitarias cuando se trata de código científico. Es fácil cometer un error al traducir un conjunto complejo de ecuaciones en un artículo de revista científica a código. Los científicos e ingenieros a menudo son programadores extremadamente pobres; La revisión por pares puede detectar fealdades arquitectónicas que hacen que el código sea difícil de mantener / comprender / usar.
David Hammen
5

Además de los buenos consejos que ya están aquí, es posible que desee considerar el propósito de su programación y, por lo tanto, lo que es importante para usted.

"Se trata más de matemáticas o ciencias que estoy probando o investigando con la programación".

Si el propósito es experimentar y probar algo para su propia comprensión y sabe cuáles deberían ser los resultados, entonces su código es básicamente un descarte rápido y su enfoque actual puede ser suficiente, aunque podría mejorarse. Si los resultados no son los esperados, puede volver y revisar.

Sin embargo, si los resultados de su codificación informan la dirección de su investigación y no sabe cuáles deberían ser los resultados , entonces la corrección se vuelve particularmente importante. Un error en su código podría llevarlo a sacar conclusiones incorrectas de su experimento con una variedad de malas implicaciones para su investigación general.

En ese caso, dividir su código en funciones fácilmente comprensibles y verificables con pruebas unitarias le proporcionará ladrillos de construcción más sólidos, lo que le dará más confianza en sus resultados y puede evitarle mucha frustración más adelante.

Aidan
fuente
5

Tan bueno como el control de versiones y las pruebas unitarias son para mantener su código general organizado y funcional, ninguno de los dos le ayuda a escribir un código más limpio.

  • El control de versiones le permitirá ver cómo y cuándo el código se volvió tan complicado como es.
  • Las pruebas unitarias se asegurarán de que, a pesar de que el código sea un desastre total, aún funcione.

Si desea evitar escribir código desordenado, necesita una herramienta que funcione donde ocurre el desorden: cuando está escribiendo el código. Un tipo popular de herramienta que se llama linter. No soy un desarrollador de Python, pero parece que Pylint podría ser una buena opción.

Un linter analiza el código que ha escrito y lo compara con un conjunto configurable de mejores prácticas. Si el linter tiene una regla que deben ser las variables camelCase, y usted escribe una snake_case, marcará eso como un error. Los buenos linters tienen reglas que van desde "se deben usar variables declaradas" hasta "La complejidad ciclomática de las funciones debe ser menor que 3".

La mayoría de los editores de código se pueden configurar para ejecutar un linter cada vez que guarde, o solo generalmente mientras escribe, e indique problemas en línea. Si escribe algo como x = 7, xse resaltará, con una instrucción para usar un nombre mejor y más largo (si eso es lo que ha configurado). Esto funciona como un corrector ortográfico en la mayoría de los procesadores de texto, lo que hace que sea difícil de ignorar y ayuda a desarrollar mejores hábitos.

Mike Gossmann
fuente
Esto debería tener muchos más votos a favor. 1
Heather
2
Pero, por el amor de Dios, asegúrate de saber cómo configurar el linter al estilo que más te guste, de lo contrario te volverá loco con su alboroto.
DrMcCleod
4

Todo lo que enumeró es una herramienta en la caja de herramientas metafórica. Como cualquier cosa en la vida, diferentes herramientas son apropiadas para diferentes tareas.

En comparación con otros campos de ingeniería, el software funciona con un montón de piezas individuales que, por sí mismas, son bastante simples. Una declaración de asignación no se evalúa de manera diferente dependiendo de las fluctuaciones de temperatura de la habitación. Una ifdeclaración no se corroe en su lugar y sigue devolviendo lo mismo después de un tiempo. Pero debido a que los elementos individuales son tan simples y el software creado por humanos, esos elementos se combinan en piezas cada vez más grandes hasta que el resultado se vuelve tan grande y complejo que alcanza los límites de lo que las personas pueden manejar mentalmente.

A medida que los proyectos de software crecieron y crecieron, las personas los estudiaron y crearon herramientas para tratar de manejar esa complejidad. OOP es un ejemplo. Más y más lenguajes de programación abstractos son otro medio. Debido a que gran parte del dinero en software está haciendo más y más , las herramientas para lograr eso son lo que va a ver y leer. Pero parece que esas situaciones no se aplican a usted.

Por lo tanto, no sienta que necesita hacer nada de eso. Al final del día, el código es solo un medio para un fin. Desafortunadamente, lo que mejor le dará la perspectiva correcta sobre lo que es y lo que no es apropiado es trabajar en algunos proyectos más grandes, ya que es mucho más difícil saber lo que falta cuando la caja de herramientas es su mente.

En cualquier caso, no me preocuparía por no usar OOP u otras técnicas siempre que sus scripts sean pequeños. Muchos de los problemas que describió son solo habilidades organizativas profesionales generales, es decir, no perder un archivo antiguo es algo con lo que todos los campos tienen que lidiar.

whatsisname
fuente
4

Además de todas las buenas sugerencias proporcionadas hasta ahora, una práctica que aprendí con el tiempo y que considero esencial es agregar de manera muy liberal comentarios detallados a su código. Es lo más importante para mí cuando vuelvo a algo después de un largo lapso de tiempo. Explícate lo que estás pensando. Lleva un poco de tiempo hacerlo, pero es relativamente fácil y casi sin dolor.

A veces tengo dos o tres veces más líneas de comentarios que del código, especialmente cuando los conceptos o técnicas son nuevos para mí y me explican.

Controle la versión, mejore sus prácticas, etc. ... todo lo anterior. Pero explíquese las cosas a medida que avanza. Funciona muy bien

Bob Newell
fuente
4

¿Qué cualidades son importantes para este tipo de programa?

Probablemente no importa si es fácil de mantener o evolucionar, porque lo más probable es que eso no suceda.

Probablemente no importa cuán eficiente sea.

Probablemente no importa si tiene una excelente interfaz de usuario o si es segura contra atacantes maliciosos.

Puede importar que sea legible: que alguien que lea su código pueda convencerse fácilmente de que hace lo que dice hacer.

Ciertamente importa que sea correcto. Si el programa da resultados incorrectos, esas son sus conclusiones científicas fuera de la ventana. Pero solo necesita procesar correctamente la entrada que realmente le está pidiendo que procese; realmente no importa mucho si se cae si se le dan valores de datos de entrada negativos, si todos sus valores de datos son positivos.

También importa que mantenga cierto nivel de control de cambios. Sus resultados científicos deben ser reproducibles, y eso significa que necesita saber qué versión del programa produjo los resultados que pretende publicar. Debido a que solo hay un desarrollador, el control de cambios no necesita ser muy elaborado, pero debe asegurarse de que puede regresar a un punto en el tiempo y reproducir sus resultados.

Así que no se preocupe por los paradigmas de programación, la orientación a objetos, la elegancia algorítmica. Preocúpese por la claridad y la legibilidad y por la trazabilidad de sus cambios a lo largo del tiempo. No te preocupes por la interfaz de usuario. No se preocupe por probar todas las combinaciones posibles de parámetros de entrada, pero haga suficientes pruebas para estar seguro (y para que otros confíen) en que sus resultados y conclusiones son válidos.

Michael Kay
fuente
4

He trabajado en un entorno similar con académicos que escriben mucho código (matemático / científico) pero su progreso es lento debido a las mismas razones que usted describe. Sin embargo, he notado una cosa particular que salió bien que creo que también puede ayudarlo: crear y mantener una colección de bibliotecas especializadas que se pueden usar en múltiples proyectos. Estas bibliotecas deberían proporcionar funciones de utilidad y, por lo tanto, ayudarán a mantener su proyecto actual específico para el dominio del problema.

Por ejemplo, puede que tenga que lidiar con muchas transformaciones de coordenadas en su campo (ECEF, NED, lat / lon, WGS84, etc.), por lo que una función como convert_ecef_to_ned()debería entrar en un nuevo proyecto llamado CoordinateTransformations. Ponga el proyecto bajo control de versiones y hágalo en los servidores de su departamento para que otras personas puedan usarlo (y con suerte mejorarlo). Luego, después de unos años, debe tener una colección robusta de bibliotecas con sus proyectos que solo contengan código específico para un problema particular / dominio de investigación.

Algunos consejos más generales:

  • Siempre trate de modelar su problema particular con la mayor precisión posible, sin importar cuál sea. De esa manera, las preguntas de diseño de software como qué / dónde / cómo colocar una variable deberían ser mucho más obvias para responder.
  • No me molestaría con el desarrollo basado en pruebas, ya que el código científico describe ideas y conceptos y es más creativo y fluido; no hay API para definir, servicios para mantener, riesgos para el código de otras personas al cambiar la funcionalidad, etc.
jigglypuff
fuente
No dejes que otras personas lo mejoren. Lo más probable es que no entiendan el propósito del código y simplemente estropearán las cosas.
mathreadler
@mathreadler Bueno, si son bibliotecas de utilidades generales, entonces será un poco difícil para otros confundirse, esa es la idea.
jigglypuff
¿Por qué es difícil estropear las bibliotecas de uso general? No es tan difícil si no tienes idea de lo que estás haciendo, o si te esfuerzas mucho, ya sea.
mathreadler
@mathreadler Debido a que generalmente solo hay una forma de hacer transformaciones coordinadas o conversiones de unidades, por ejemplo.
jigglypuff
Por lo general, hay muchas formas, dependiendo de cómo se almacenan sus números en la memoria, qué representación usan y muchas otras cosas, para qué CPU tiene la intención de compilar la biblioteca. Un codificador puede suponer que todos siempre usarán dobles IEEE, por ejemplo, pero otro casi siempre usa precisión única o algún tercer formato más extraño. Un codificador usará entonces el polimorfismo de plantilla, pero otro podría ser alérgico a él, y un tercero, incluso más extraño, codificará todo en un nivel bajo de ensamblaje.
mathreadler
3

Las siguientes son mis opiniones y están muy influenciadas por mi propio camino particular.

La codificación a menudo genera perspectivas dogmáticas sobre cómo debe hacer las cosas. En lugar de técnicas y herramientas, creo que debe analizar los valores y costos acumulativos para decidir una estrategia adecuada.

Escribir un código sólido, legible, depurable y sólido requiere mucho tiempo y esfuerzo. En muchos casos, dado un horizonte de planificación limitado, no vale la pena hacerlo (parálisis de análisis).

Un colega tenía una regla general; si está haciendo esencialmente el mismo tipo de cosas por tercera vez, invierta el esfuerzo, de lo contrario, un trabajo rápido y sucio es apropiado.

Las pruebas de algún tipo son esenciales, pero para proyectos únicos, simplemente la observación puede ser suficiente. Para cualquier cosa sustancial, las pruebas y la infraestructura de prueba son esenciales. El valor es que lo libera al codificar, el costo es que si la prueba se enfoca en una implementación particular, entonces las pruebas también necesitan mantenimiento. Las pruebas también le recuerdan cómo se supone que funcionan las cosas.

Para mis propios guiones únicos (a menudo para cosas como validar una estimación de probabilidad o similar), encontré dos cosas pequeñas muy útiles: 1. Incluya un comentario que muestre cómo se usa el código. 2. Incluya una breve descripción de por qué escribió el código. Estas cosas son terriblemente obvias cuando escribes el código, pero la obviedad desperdicia tiempo :-).

OOP se trata de reutilizar código, abstraer, encapsular, factorizar, etc. Muy útil, pero fácil de perder si la producción de código y diseño de calidad no es su objetivo final. Se necesita tiempo y esfuerzo para producir cosas de calidad.

cobre.hat
fuente
3

Si bien creo que las pruebas unitarias tienen sus méritos, tienen un valor dudoso para el desarrollo científico: a menudo son demasiado pequeñas para ofrecer mucho valor.

Pero realmente me gustan las pruebas de integración para el código científico:

Aísle una pequeña porción de su código que podría funcionar por sí mismo, por ejemplo, la tubería ETL. Luego escriba una prueba que proporcione los datos, ejecute la tubería etl (o solo un paso) y luego pruebe que el resultado coincida con sus expectativas. Si bien el fragmento probado puede ser un montón de código, la prueba proporciona aún valor:

  1. Tiene un gancho conveniente para volver a ejecutar su código, lo que ayuda a ejecutarlo con frecuencia.
  2. Puede probar algunos supuestos en su prueba
  3. Si algo se rompe, es fácil agregar una prueba fallida y hacer la solución
  4. Usted codifica las entradas / salidas esperadas, evitando el dolor de cabeza habitual que resulta de intentar adivinar el formato de datos de entrada.
  5. Si bien no es tan simple como las pruebas unitarias, las pruebas de TI aún ayudan a separar su código y lo obligan a agregar algunos límites en su código.

Estoy usando esta técnica a menudo, y a menudo termino con una función principal relativamente legible, pero las subfunciones suelen ser bastante largas y feas, pero pueden modificarse y reorganizarse rápidamente debido a los límites de E / S robustos.

Christian Sauer
fuente
2

Normalmente trabajo en una base de fuente muy grande. Usamos todas las herramientas que mencionas. Recientemente, comencé a trabajar en algunos scripts de Python para un proyecto paralelo. Son unas pocas docenas a unos cientos de líneas como máximo. Por costumbre, confié mis guiones al control de fuente. Esto ha sido útil porque puedo crear ramas para probar experimentos que podrían no funcionar. Puedo bifurcar si necesito duplicar el código y modificarlo para otro propósito. Esto deja el original intacto en caso de que necesite volver a sacarlo.

Para las "pruebas unitarias" solo tengo algunos archivos de entrada que están destinados a producir una salida conocida que verifico a mano. Probablemente podría automatizarlo, pero parece que tomaría más tiempo hacerlo de lo que ahorraría al hacerlo. Probablemente depende de con qué frecuencia tengo que modificar y ejecutar los scripts. De cualquier manera, si funciona, hazlo. Si es más problema de lo que vale, no pierdas tu tiempo.

usuario1118321
fuente
2

Con la escritura de código, como con la escritura en general, la pregunta principal es:

¿Qué lector tienes en mente? o ¿Quién consume tu código?

Cosas como las pautas formales de codificación no tienen sentido cuando eres tu único público.

Dicho esto, por otro lado, sería útil escribir el código de una manera, su futuro es capaz de entenderlo de inmediato.

Por lo tanto, un "buen estilo" sería el que más le ayuda. El aspecto que debería tener ese estilo es una respuesta que no puedo dar.

Creo que no necesita OOP o pruebas unitarias para archivos de 150 LOC. Un VCS dedicado sería interesante cuando tienes un código en evolución. De lo contrario, a .bakhace el truco. Estas herramientas son una cura para una enfermedad, es posible que ni siquiera tenga.

Tal vez debería escribir su código de tal manera que, incluso si lo lee mientras está borracho, puede leerlo, comprenderlo y modificarlo.

Thomas Junk
fuente