Como mejorar tu script fácilmente

Esta entrada apareció originalmente en inglés en mi blog.

Nos ha pasado a todos. Ese momento en el que descubres que sabes suficiente sobre un lenguage de programacion que quieres ponerlo en práctica y construir "algo", lo que sea.
Una de las mejores cosas de la comunidad de Python es no sólo su habilidad para construir cosas increíbles, sino también para compartirlas con todo el mundo, mejorando la comunidad en el proceso.

Sin embargo, llevo un tiempo fijándome en un patrón que se repite en algunos de estos proyectos. Seguro que has visto alguno así. Hablo de esos proyectos con 2 ó 3 componentes, donde el README tiene una pequeña descripción del proyecto, quizás un par de lineas explicando como ejecutar el proyecto, y frases del tipo, "Seguramente añadiré X o Y si tengo tiempo".

El caso es que muchos de estos proyectos son realmente interesantes, y tienen algún tipo de componentes que me gustaría usar sin tener que implementarlos yo mismo.

Te voy a mostrar 3 formas distintas de implementar uno de estos proyectos, cada una de ellas mejor (desde mi punto de vista) que la anterior:

Supongamos que queremos construir un script genial, donde la funcionalidad principal será que, dado un número entero por el usuario, realizará un calculo simple en base a ese entero, y devolverá el resultado.

Implementación 1

 
#!/usr/bin/env python

"""
Super awesome script
Asks the user for a number:
 - If the number is less or equal to 100, it returns the 1st tetration of the number (power of itself)
 - else, it returns the number squared
"""

__version__ = '0.1'

if __name__ == '__main__':

    while 1:
        user_number = input('Choose a number:\n') #raw_input() in python2
        if user_number.isdigit():
            user_number = int(user_number)
            break
        else:
            print('{} is not a valid number'.format(user_number))

    if user_number > 100:
        print(user_number**2)
    else:
        print(user_number**user_number)

Ésta suele ser la implementación de alquien que lleva poco tiempo en python. Funciona, pregunta al usuario por el input, realiza la operación, e imprime en pantalla el resultado.

Veo dos problemas en esta implementación:

1. No hay ningún tipo de separación entre la lógica de la interacción del usuario y la lógica del cálculo. Todo esta incluido en el mismo macro bloque. Pese a ser funcional, esta implementación hace que sea díficil el modificar o expandir este script (para hacerlo tendrías que leerte todo el código).

2. Estamos gestionando toda la validación por nuestra cuenta. Python tiene formas de hacer esto para que tú no te tengas que molestar en hacerlo :).

Para la siguiente implementación, usaremos el módulo mas simple de la libreria standard para trabajar con inputs del usuario, .

Implementación 2

 
#!/usr/bin/env python

"""
Super awesome script
Asks the user for a number:
 - If the number is less or equal to 100, it returns it to the power of itself
 - else, it returns the number squared
"""

import argparse

__version__ = '0.2'


if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('--number', required=True, type=int,
                        help='number to perform calculation')
    values = parser.parse_args()
    user_number = values.number
    if user_number > 100:
        print(user_number**2)
    else:
        print(user_number**user_number)

En esta implementación hemos eliminado el problema #2 de la implementación anterior. En esta ocasión usamos argparse, de esta forma dejamos que la libreria estándar se encargue de la validación del input. Esta implementación no funciona a menos que el input sea válido.

Todavía tenemos el problema #1, la separación entre la lógica del input y la lógica primaria (la función de calculo).

En la siguiente implementación vemos como podemos arreglar esto.

Implementación 3

 
#!/usr/bin/env python

"""
Super awesome script
Asks the user for a number:
 - If the number is less or equal to 100, it returns it to the power of itself
 - else, it returns the number squared
"""

import argparse

__version__ = '0.3'



def calculation(number):
    """Performs awesome calculation"""
    if number > 100:
        return number**2
    else:
        return number**number

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('--number', required=True, type=int,
                        help='number to perform calculation')
    values = parser.parse_args()
    user_number = values.number
    calculation_result = calculation(user_number)
    print(calculation_result)

En esta implementación, hemos hecho dos cosas:

1. Hemos puesto la carga de la validación en un módulo bien mantenida como es argparse.
2. Hemos separado la lógica del input del usuario de la lógica del input de cálculo.

Éste último cambio tiene tres ventajas sobre #1 y #2.

- Ventaja 1: En primer lugar, si nos damos cuenta que por algún motivo queremos modificar el 100 por un 200, ahora podemos fácilmente modificar eso, sin tener que modificar ni leer todo el código. Siempre y cuando la función calculation siga teniendo los mismos inputs y outputs, el resto de código seguirá funcionando sin problemas.

- Ventaja 2: Otro efecto, y para mi el más significativo, es que si ahora yo leo este script que otra persona ha escrito, y me gusta tanto que quiero añadirlo a un proyecto mio, ¡ahora puedo importarlo sin problemas!.

En las implementacines #1 y #2, la única manera de usar el script era haciendo:

python calculation_script.py --number INTEGER

Ahora, en la implementación #3, tenemos una manera mucho mas útil de usar la lógica mas importante (la del cálculo). Si yo tengo otro script en el que quiero usar la funcion de cálculo, puedo usarla de la forma:

 
from calculation_script import calculation

number = 10
calculation_result = calculation(number)

¿Increíble, no? Simplemente haciendo una pequeña modificación a la estructura del proyecto, ahora cualquier persona se puede beneficiar del mismo.

- Ventaja 3: Supongamos que este simple proyecto empieza a crecer, más desarrolladores se interesan y empiezan a colaborar. El código empieza a crecer y alguien comenta que tendría sentido empezar a trabajar en el suite de testing. (si no sabes lo que es el testing, te recomiendo este artículo.)

Con la implementación #3, testear la funcionalidad de calculation es super fácil (gracias a /u/choffee en reddit por el apunte):

 
import pytest
from calculation_script import calculation

class TestCalculation:
    """Calculation function does funky things to number
    More above 100 than below
    """
    def test_zero():
        x = 0
        assert calculation(x) == 0

    def test_border():
        x = 100
        assert calculation(x) == 10000

    def test_one():
        x = 1
        assert calculation(x) == 1

Piensa en ello la próxima vez, no cuesta nada y hace que tu script sea mejor :)

¿Quieres empezar con Python?

Voy a intentar hacer una  pequeña guía para iniciarse en Python que, más allá de ser una compilación de enlaces a muchos sitios, pretende ser útil dentro de la abrumadora cantidad de información disponible ahí fuera.

¿No sabes lo que es un sistema de control de versiones, ni el desarrollo por pruebas, ni la programación orientada a objetos? No te preocupes ahora ni te vuelvas loco a ponerte a buscarlo por internet, muchas de las cosas y conceptos que pongo en esta mini guía se adquieren con el tiempo y la experiencia.

Paso 0. Python 2 o Python 3.

Esta es una duda importante que tienen muchos recien llegados al lenguaje. En Python existen dos versiones oficiales en mantenimiento, la versión 2 se seguirá manteniendo durante unos cuantos años y se usa bastante debido a que hay mucho código que depende de esta versión, la versión 3 salió hace unos cuantos años y es la actual rama de desarrollo y la que va evolucionando por lo que es el presente y futuro de Python. Rompe la compatibilidad con la versión 2 en algunas cosas pero hay formas de hacer código que sea compatible tanto con Python 2 como con Python 3 sin excesivos problemas.

Si partes de cero en el lenguaje te recomiendo encarecidamente que empieces por Python 3 y no mires nunca más para atrás. Si dependes de código que no funciona en Python 3 te recomiendo que lo hagas funcionar en Python 3 porque Python 2, más pronto que tarde, el tiempo pasa volando, se dejará de mantener y estarás listo para ese momento. No es tan difícil y nos puedes preguntar para que te ayudemos.

Paso 1. El tutorial oficial.

El tutorial oficial lo tenéis disponible en español traducido por la gente de Python Argentina.

Aquí te explicarán como instalar Python (si tienes dudas pregúntanos) y el tutorial será donde empezarás a ver la sintaxis de Python, como hacer bucles, condicionales, asignar valores, crear funciones,...

En este paso no pierdas el tiempo buscando herramientas y cosas así, de hecho, es contraproducente. Cualquier editor de texto simple es más que suficiente y recomendable para empezar: vim, emacs, el bloc de notas de tu windows, Geany, TextPad,... ¡¡No necesitas nama más!!

Paso 2. Ponte a programar.

Una vez leído el tutorial no mires más información ni busques en google e imponte un proyecto sencillo para empezar. Te pongo algunos ejemplos:

  • Un algoritmo para ordenar palabras.
  • Un conversor de grados celsius a Farenheit o viceversa (o Julios a calorias, o km/h a m/s,...).
  • Abrir un fichero de texto, por ejemplo un csv, leerlo y a una columna de números sumarle 1000 a cada uno de los valores.
  • Cualquier problema sencillo del proyecto Euler.
  • ...

Si no eres capaz de resolverlo solo con el tutorial oficial ve al paso 3.

Paso 3. Internet.

Es un pozo infinito de información. Por suerte hay varios sitios disponibles para poder preguntar de forma más acotada.

En español:

En inglés:

Recomendaciones de uso:

En todos los sitios que he enlazado en este paso podéis hacer preguntas. Las preguntas las responden personas que tienen ganas de ayudarte pero que también son gente ocupada que tienen cosas importantes que hacer.

  1. Primero deberías de buscar si tu pregunta ya ha sido hecha (lo más seguro) y ver si las respuestas que dieron te resuelven el problema. Si ya tienes la solución, perfecto, no has hecho perder el tiempo de nadie y tú ya puedes continuar.
  2. Si no has encontrado una respuesta, formula la pregunta en un único sitio (todos son buenas opciones) y procura dar el mayor número de detalles posibles para que la gente entienda tu problema. Si tienes código, enséñalo para que los demás puedan ver qué has intentado hacer o en qué punto te encuentras. Un ejemplo de pregunta mal formulada y que no será bienvenida la puedes encontrar aquí.
  3. Algunos han respondido tu pregunta. Te solucionen o no la vida, por favor, sé agradecido, la comunidad se convierte en más amena e inclusiva si todos somos educados y agradecidos. Esas personas han perdido su tiempo y han usado conocimientos que les ha costado un tiempo adquirir para intentar ayudarte de forma desinteresada.
  4. Alguien se muestra soberbio y/o maleducado. DON'T FEED THE TROLL.

Paso 4. Ya eres capaz de resolver pequeños problemas.

Empieza a profundizar en temas más avanzados: Programación orientada a objetos, organización del código para hacerlo más mantenible/legible, desarrollo dirigido por pruebas,...

Algunos libros libres y gratis para ver cosas un poco más avanzadas:

  • Dive into Python. Empieza por lo básico pero poco a poco va profundizando en cosas un poco más avanzadas. como orientación a objetos,  testing,... (EN INGLÉS).
  • Think Python. Este libro está escrito por un profesor y basado en su experiencia enseñando Python. (EN INGLÉS).

Paso 5. De Padawan a Jedi.

Crea pequeños proyectos, métete a colaborar en algún proyecto de código libre, comparte código, ayuda a jóvenes padawanes a convertirse en Jedis, da pequeñas charlas,... Todo ello te permite salir de tu zona de confort y te obliga a aprender.

Existen varios sitios para compartir proyectos y código. Entre los más conocidos tenéis:

  • BitBucket. Permite tener repositorios de código privados o públicos, funciona con varios sistemas de control de versiones (Mercurial y Git).
  • Github. Es el más famoso, el facebook del código. No te permite tener repositiorios privados si no es pagando o solo te permite interactuar con los repositorios usando Git.

Lee código. En muchas ocasiones no hay más remedio que leer código de otros, a ratos puede ser poco agradable pero te permite conocer cómo otros son capaces de resolver sus problemas, cómo aplican determinados patrones, cómo organizan su código, que librerías usan que tú desconocías,...

Acércate a tu grupo local. Estamos hablando de Python pero hay grupos locales de muchas cosas. Algunos relacionados con Python en España los puedes encontrar en:

Sé curioso para seguir aprendiendo.

¿Has llegado al último paso?

Espero que hayas disfrutado este viaje que, seguramente, te haya ocupado días (no vaciles), semanas (te quiero conocer), meses (eres muy aplicado) o años (tú eres de los mios). Poco más se puede decir que no sepas ya resolver por ti mismo.

P.D.: En ningún momento menciono cursos MOOC porque, en mi modesta opinión, muchas veces no se ajustan a las necesidades reales del alumno, tienen saltos importantes de conocimiento, a veces el guión a seguir no está claro, son excesivamente básicos o complejos,... Todo esto, en algunas ocasiones provoca pérdidas de tiempo y frustración. Puedo estar equivocado y no te costará encontrar cursos MOOC para aprender a programar.

P.D.2: Si quieres incluir algo más o crees que algo es incorrecto, lo podemos discutir en los comentarios y llegado el caso, actualizo esta mini guía.