2.8. Todo junto

La última línea de código, la única que no hemos desmenuzado todavía, es la que hace todo el trabajo. Pero el trabajo ya es fácil, porque todo lo que necesitamos ya está dispuesto de la manera en que lo necesitamos. Las fichas del dominó están en su sitio; lo que queda es golpear la primera.

Ejemplo 2.25. El meollo de apihelper.py

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

Advierta que esto es una sola orden, dividida en varias líneas, pero sin utilizar el carácter de continuación de línea (“\”). ¿Recuerda cuando dije que algunas expresiones pueden dividirse en varias líneas sin usar la contrabarra? Una lista por comprensión es una de estas expresiones, ya que la expresión completa está entre corchetes.

Vamos a tomarlo por el final y recorrerlo hacia atrás. El fragmento


for method in methodList

nos muestra que esto es una lista por comprensión. Como ya sabe, methodList es una lista de todos los métodos que nos interesan en object. De modo que estamos recorriendo esta lista con la variable method.

Ejemplo 2.26. Obtención de una cadena de documentación de forma dinámica

>>> import odbchelper
>>> object = odbchelper                   1
>>> method = 'buildConnectionString'      2
>>> getattr(object, method)               3
<function buildConnectionString at 010D6D74>
>>> print getattr(object, method).__doc__ 4
Build a connection string from a dictionary of parameters.

    Returns string.
1 En la función help, object es el objeto sobre el que pedimos ayuda, pasado como argumento.
2 Cuando recorremos methodList, method es el nombre del método actual.
3 Usando la función getattr, obtenemos una referencia a la función método del módulo objeto.
4 Ahora, mostrar la cadena de documentación del método es fácil.

La siguiente pieza del puzzle es el uso de str con la cadena de documentación. Como recordará usted, str es una función incorporada que convierte datos en cadenas. Pero una cadena de documentación es siempre una cadena, luego ¿por qué molestarse en usar la función str? La respuesta es que no todas las funciones tienen una cadena de documentación, y en este caso su atributo __doc__ es None.

Ejemplo 2.27. ¿Por qué usar str con una cadena de documentación?

>>> {}.keys.__doc__         1
>>> {}.keys.__doc__ == None 2
1
>>> str({}.keys.__doc__)    3
'None'
1 La función keys de un diccionario no tiene cadena de documentación, luego su atributo __doc__ es None. Para más confusión, si se evalúa directamente el atributo __doc__, el entorno de programación Python no muestra nada en absoluto, lo cual tiene sentido si se piensa bien, pero es poco práctico.
2 Se puede verificar que el valor del atributo __doc__ es realmente None comparándolo directamente.
3 Con el uso de la función str se toma el valor nulo y se devuelve su representación como cadena, 'None'.
Nota
En sql, se utiliza IS NULL en vez de = NULL para comparar un valor nulo. En Python no hay una sintaxis especial; se usa == None como en cualquier otra comparación.

Ahora que estamos seguros de tener una cadena, podemos pasarla a processFunc, que hemos definido como una función que unifica o no el espacio en blanco. Ahora se ve por qué es importante utilizar str para convertir el valor None en su representación como cadena. processFunc asume que su argumento es una cadena y llama a su método split, el cual fallaría si le pasamos None, ya que None no tiene un método split.

Yendo más atrás, vemos que estamos utilizando el formato de cadenas de nuevo para concatenar el valor de retorno de processFunc con el valor de retorno del método ljust de method. Este es un nuevo método de cadena que no hemos visto antes.

Ejemplo 2.28. Presentación del método ljust

>>> s = 'buildConnectionString'
>>> s.ljust(30) 1
'buildConnectionString         '
>>> s.ljust(20) 2
'buildConnectionString'
1 ljust rellena la cadena con espacios hasta la longitud indicada. Esto lo usa la función help para hacer dos columnas y alinear todas las cadenas de documentación en la segunda columna.
2 Si la longitud indicada es menor que la longitud de la cadena, ljust devuelve simplemente la cadena sin cambios. Nunca corta la cadena.

Casi hemos terminado. Dados el nombre del método (relleno con espacios con el método ljust) y la cadena de documentación (posiblemente con el espacio blanco unificado), que resultó de la llamada a processFunc, concatenamos las dos y obtenemos una única cadena. Como estamos recorriendo methodList, terminamos con una lista de cadenas. Utilizando el método join de la cadena "\n", unimos esta lista en una única cadena, con cada elemento de la lista en una línea diferente, y mostramos el resultado.

Ejemplo 2.29. Muestra de una lista

>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) 1
a
b
c
1 Este es también un útil truco de depuración cuando se trabaja con listas. Y en Python, siempre se trabaja con listas.

Esta es la última pieza del puzzle. Este código debería ahora entenderse perfectamente.

Ejemplo 2.30. Otra vez el meollo de apihelper.py

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])