introduccion a python para ingenieros

45
Introducción a Python para ingenieros Estos breves apuntes son el material de apoyo de un curso de 10 horas sobre el lenguaje de programación Python aplicado al ámbito de la Ingeniería. Este curso está dirigido a alumnos de últimos cursos de carrera o de postgrado que ya cuentan con alguna experiencia en programación, bien con C o Fortran, y con conocimientos de Matlab. Este no es ningún caso un curso completo de Python. Es demasiado corto y parcial como para servir de referencia. El objetivo es el de poder reciclar los conocimientos de Cálculo Numérico y programación de la manera más eficiente posible para poder escribir programas y librerías en Python. Es, por consiguiente, un curso de mínimos. Introducción ¿Qué es Python? Documentación El entorno de desarrollo en Matlab SAGE Python 3 Primeros pasos con Python Python es un lenguaje interpretado Python es un lenguaje interactivo Python es un lenguaje dinámico Python es un lenguaje orientado a objetos En Python todo está modularizado Python incluye baterías, pero no cargador Python es también una calculadora Control de flujo Condicionales o sentencias if Intermezzo. Listas. Iteradores Definición de funciones, encapsulamiento y módulos Duck Typing Docstrings Módulos Intermezzo. Tuples Funciones que retornan varias variables Funciones con argumentos por omisión Empaquetar y desempaquetar argumentos Intermezzo. Diccionarios. De vuelta al empaquetado y desempaquetado de argumentos Funciones lambda Programación funcional Tipos, o cómo programar en Python La clase string Intermezzo. Archivos y la clase file La clase list Una breve introducción a la programación orientada a objetos Un pequeño viaje por la biblioteca estándar. El módulo os

Upload: gastromono

Post on 25-Jul-2015

490 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Introduccion a Python Para Ingenieros

Introducción a Python para ingenierosEstos breves apuntes son el material de apoyo de un curso de 10 horas sobre el lenguaje de programación Python aplicado al ámbito de la Ingeniería. Este curso está dirigido a alumnos de últimos cursos de carrera o de postgrado que ya cuentan con alguna experiencia en programación, bien con C o Fortran, y con conocimientos de Matlab.

Este no es ningún caso un curso completo de Python. Es demasiado corto y parcial como para servir de referencia. El objetivo es el de poder reciclar los conocimientos de Cálculo Numérico y programación de la manera más eficiente posible para poder escribir programas y librerías en Python. Es, por consiguiente, un curso de mínimos.

• Introducción • ¿Qué es Python? • Documentación • El entorno de desarrollo en Matlab • SAGE • Python 3

• Primeros pasos con Python • Python es un lenguaje interpretado • Python es un lenguaje interactivo • Python es un lenguaje dinámico • Python es un lenguaje orientado a objetos • En Python todo está modularizado • Python incluye baterías, pero no cargador • Python es también una calculadora

• Control de flujo • Condicionales o sentencias if • Intermezzo. Listas. • Iteradores

• Definición de funciones, encapsulamiento y módulos • Duck Typing • Docstrings • Módulos • Intermezzo. Tuples • Funciones que retornan varias variables • Funciones con argumentos por omisión • Empaquetar y desempaquetar argumentos • Intermezzo. Diccionarios. • De vuelta al empaquetado y desempaquetado de argumentos • Funciones lambda • Programación funcional

• Tipos, o cómo programar en Python • La clase string • Intermezzo. Archivos y la clase file • La clase list

• Una breve introducción a la programación orientada a objetos • Un pequeño viaje por la biblioteca estándar.

• El módulo os

Page 3: Introduccion a Python Para Ingenieros

IntroducciónEstaría bien que por una vez leyerais la introducción, aunque todos sabemos que nadie se lee nunca la introducción de nada.

¿Qué es Python?Python es un lenguaje de programación interpretado e interactivo de propósito general. Es, hasta cierto punto, comparable con otros lenguajes de programación de dominio específico que podemos encontrar dentro del ámbito de la Ingeniería como Matlab, Octave, R, SPSS o IDL.

Se trata también de un lenguaje de programación relativamente moderno y en constante, aunque moderada, renovación. Fue creado por Guido van Rossum en el año 1991 tomando prestadas muchas de las buenas ideas presentes en los lenguajes de programación que conocía. A diferencia de lo que viene siendo habitual, en vez de reinventar cada idea simplemente las incorporó de manera que tuvieran sentido.

Aunque la mente de un holandés suele ser un sitio bastante complicado y retorcido consiguió crear un lenguaje sencillo, intuitivo y fácil de aprender. Difícilmente se es más productivo con cualquier otro lenguaje de cuanto se es programando en Python. De hecho se suele decir: la vida es corta, por eso programo en Python. Quizás el único lenguaje comparable a Python en ese sentido es Ruby, que curiosamente nació de la mente de un japonés; también un sitio habitualmente complicado y retorcido.

Python gozaba de cierta popularidad dentro del mundo UNIX porque se le consideraba una alternativa razonable a Perl, el que era por aquel entonces el lenguaje de scripting para programación de sistemas por antonomasia. La explosión de Python llegó entre los años 2003 y 2007 con el auge de las aplicaciones web y posteriormente con la nube. Es uno de los cuatro lenguajes oficiales de Google y toda la infraestructura de Youtube está programada en Python. Permite a los desarrolladores de arquitecturas de servicios utilizar el mismo lenguaje para la aplicación (para lo que también se ha venido utilzando PHP, como en el caso de Facebook), para el middleware y para la gestión de los equipos. Si bien Java es el lenguaje de las aburridas consultorías de sistemas y de las mastodónticas multinacionales, Python es el lenguaje de las startups de Silicon Valley.

Más o menos mientras ganaba en popularidad entre los futuros empleados de Google, en algunos sectores del cálculo científico se lo veia como una más que prometedora alternativa a Matlab. Matlab es en el fondo un lenguaje de programación mediocre que sirve para juntar funciones realmente útiles. Python debía recorrer el sentido contrario: es un lenguaje de programación singularmente atractivo para el que, hace unos años, no había un contexto científico. Ni siquiera se diseñó pensando en el Cálculo Numérico.

Jim Hugunin puso la primera piedra del castillo, Numeric. No era más que una clase para poder tratar arrays n-dimensionales en Python y algunas rutinas numéricas pero tenía serios problemas de velocidad en comparación con Matlab. También había ciertos problemas de fragmentación, cada centro de investigación desarrollaba sus propias bibliotecas de cálculo y las compartía, pero no había ningún lugar donde poder poner las cosas en común. El punto de no retorno llegó el año 2007 con numpy y scipy. Finalmente Python contaba con los bloques básicos para hacer Cálculo Numérico, todos los usuarios usaban el mismo y sabían dónde compartir sus desarrollos.

En enero de 2012, momento en el que escribo estas líneas, Python cuenta con una colección de recursos para Ciencia equivalente a la de Matlab, incluso superior en campos como la visualización o el cálculo simbólico. Y la gran mayoría de estos recursos son libres y gratuitos, sin problemas de royalties o de abogados que intenten defender la propiedad intelectual de sus clientes.

Page 4: Introduccion a Python Para Ingenieros

En resumen: Python es el futuro.

DocumentaciónUno de los motivos del éxito de Python es la gran cantidad de documentación de calidad que existe sobre el lenguaje y sus bibliotecas, empezando por la documentación oficial que encontraremos tanto en la página web http://python.org como en los instaladores para cualquier sistema operativo.

Hay dos documentos que es importante retener en la memoria. El primero es el tutorial, de poco más de 100 páginas, que introduce la mayoría de los elementos esenciales del lenguaje. Obviamente en tan pocas páginas no es posible entrar en profundidad con todos los detalles y matices del lenguaje pero es una guía casi imprescindible si uno quiere programar en Python y no sabe cómo.

El segundo documento es mucho más extenso: la documentación de la librería estándar. Python es, como veremos, un lenguaje modular. Algunos de estos módulos se consideran parte de cualquier distribución al igual que en C tenemos la función malloc o en Fortran la función allocate. Todo lo que se documenta como parte de la librería estándar está disponible en cualquier instalación de Python independientemente del sistema operativo y de la arquitectura de procesador.

El entorno de desarrollo en MatlabPython es un lenguaje de programación con todas las letras tal como lo es C, Fortran o Java. Al igual que estos lenguajes y a diferencia de Matlab no existe un entorno de desarrollo “oficial”. Aunque en cada instalación de Python viene un pequeño editor llamado idle no es ni mucho menos el más recomendable.

En mi caso tengo dos elecciones personales: Emacs y Eclipse. Lo más normal es que si no os habéis peleado largas horas de vuestra vida con un Linux ni siquiera os suene la palabra emacs pero sí es probable que os suene eclipse.

Eclipse es un entorno de desarrollo originalmente pensado para Java pero que fue tornandos en agnóstico respecto al lenguaje de programación. Existe una extensión bastante popular llamada pydev que convierte a Eclipse en un entorno de desarrollo completo para Python, con gestor de proyectos y debugger. Es una elección muy interesante en el momento en el que uno se plantea realizar un proyecto realmente grande con Python. Pero para manejar los pequeños scripts que escribiremos en este curso cualquier cosa vale, incluso idle.

Hay centenares de entornos de desarrollo para Python, incluso algunos están pensados para parecerse lo máximo posible al entorno de Matlab como spyder. Os recomiendo que visitéis la wiki del proyecto Python donde encontraréis una lista actualizada de todos los entornos de desarrollo para Python, tanto libres como comerciales.

SAGESage es un notebook parecido al que encontramos en entornos como Maple o Mathematica basado en Python. Fue creado por William Stein para cubrir sus necesidades como docente e investigador en Matemáticas. Uno podría considerar SAGE como un proyecto paralelo y casi independiente de Python pero nos permite utilizar Python en la nube a través del navegador. De este modo no utilizaremos ninguna funcionalidad específica de SAGE sino que accederemos al intérprete de Python a través del notebook de forma interactiva y “en la nube”.

Utilizaré SAGE como soporte de este curso para ver de manera interactiva el resultado de bloques de código escrito en Python, pero esto no significa en absoluto que la manera óptima de escribir un programa en Python sea con SAGE. Se trata de una herramienta puramente docente.

El objetivo es dejar un notebook público en http://picachu.dmt.upm.es para que quien lo desee

Page 5: Introduccion a Python Para Ingenieros

pueda crear, modificar y compartir su trabajo en Python con todos los participantes del grupo.

Python 3Python está en la actualidad migrando de versión. Aunque la mayoría del código escrito en Python sigue las especificaciones de la versión 2 hace ya un tiempo que uno puede descargar y utilizar la versión 3. Algunos cambios importantes entre versiones son fáciles de migrar, como por ejemplo el comando print que pasa a ser una función. Incluso podemos pedir al intérprete de Python 2 que nos avise si alguna parte de nuestro código tendrá problemas con Python 3.

Este hecho descubre la pregunta de cuándo se hará necesario empezar a escribir código para Python 3. La respuesta es que no depende de nosotros. Cuando uno escribe en Python utiliza muchas librerías adicionales que no siempre están disponibles aún para Python 3. En el momento en el que todas las dependencias de nuestro trabajo ya ejecuten sobre Python 3 probablemente sólo tengamos que cambiar de un intérprete a otro.

Page 6: Introduccion a Python Para Ingenieros

Primeros pasos con Python

Python es un lenguaje interpretadoCuando compilamos un programa escrito en C o en Fortran generamos un ejecutable. Para hacer funcionar ese ejecutable nos basta con muy poca cosa; en el caso de un “hola, mundo” basta con simplemente ejecutarlo. El sistema operativo lo considera ejecutable y simplemente cumple sus ordenes.

Esto no sucede así en los lenguajes interpretados. El código en Python nunca llega a traducirse a algo que el sistema operativo pueda entender. En Python el programa termina convertido en un ensamblador propio que una máquina virtual es capaz de entender y ejecutar. La consecuencia principal de este método es que es imprescindible contar con un intérprete de Python instalado en el ordenador para poder ejecutar código en Python.

Esta no es hoy en día una condición demasiado severa. El único sistema operativo mayoritario que no cuenta con un intérprete de Python instalado por omisión es Windows. Linux, Mac OSX, Solaris y AIX entre otros cuentan con uno, aunque algunas veces compensa instalar una versión más actualizada que la que encontraremos en la distribución del sistema operativo. En el caso especial de Windows bastará con descargarse un instalador, darle doble clic y decir que sí a todo.

Cuando ejecutamos código en Python lo lanzamos a un intérprete que es capaz de entender este lenguaje. A diferencia de los lenguajes estáticos como C o Fortran en el que un compilador convierte el código de programa en un ejecutable que el sistema operativo es capaz de entender.

Python es un lenguaje interactivoPython dispone de una consola interactiva con la que jugaremos un poco antes de escribir algún que otro programa.

Consola de Python en la ventana de IDLE en Linux

La manera de acceder a esta consola difiere en función del sistema operativo. En los UNIX y derivados bastará con abrir una consola de sistema y teclear en ella python. En Windows bastará con abrir el programa correspondiente que seguro que se llamará Python shell o algo parecido.

Page 7: Introduccion a Python Para Ingenieros

Una vez estemos delante del intérprete de Python podemos empezar a jugar. En este respecto se trata de un lenguaje de programación parecido a Matlab, de modo que podemos probar a hacer una suma sin problemas.

>>> 2+24

Como seguramente intentaréis hacer algo más complicado con números, una gran parte de las funciones matemáticas básicas están en el módulo math, pero ya llegaremos a ello.

Python es un lenguaje dinámicoEn Python no hay que declarar ninguna variable. Cada variable toma el tipo que tenga en cada caso lo que esté en el lado derecho del operador asignación =. Esto es cierto tanto la primera vez que se utiliza una variable como cuando se le asigna un valor a una variable ya existente.

Esto es sencillo en el caso de Python porque es un lenguaje interpretado: la mayoría de las asignaciones resuelven el tipo en tiempo de ejecución, no en tiempo de compilación del mismo modo que sucede en Matlab o Octave.

Si volvemos al intérprete:

>>> a = 2.3>>> b = 3.2>>> print a*b7.36>>> a = 2>>> print type(a)<type 'int'>>>> print a*b6.4>>> a = 'hola'>>> print type(a)<type 'str'>>>> print a*bTraceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: can't multiply sequence by non-int of type 'float'

Creo que no hace falta dedicarle un capítulo a lo que hace la sentencia print.

Obviamete, cuando intentamos multiplicar una secuencia de caracteres por un número en coma flotante obtenemos un error claramente identificado como error de tipo.

Aunque Python está lleno de sorpresas. Si vuestra intución os dice que una operación puede ser posible a lo mejor está implementada. Qizás parte del éxito de Python se debe a que la gente que lo ha estado creando durante las dos últimas décadas es gente particularmente lista. Por ejemplo... ¿Qué sucede si multiplicamos una palabra por 2?

>>> a = 'hola'>>> print 2*aholahola

Pues que tenemos dos veces 'hola'. Entonces, si tomamos la definición de multiplicación como una secuencia de sumas...

>>> print a+aholahola

Python está lleno de detalles de estos así que algunas veces es bueno dejarse llevar por la intuición.

Page 8: Introduccion a Python Para Ingenieros

Python es un lenguaje orientado a objetosEl las carreras de informática cubrir los conceptos fundamentales de la orientación a objetos requiere una asignatura entera. De todos los paraidgmas de programación es el más exitoso que se conoce. Incluso Fortran, a partir del estándar Fortran 2003, soporta la programación orientada a objetos. Matlab era también otro lenguaje que históricamente había ignorado la orientación a objetos pero por soportarlo también, a su manera. La primera implementación de la orientación a objetos de Matlab era tan deficiente que quedó en el olvido. A la segunda consiguieron un resultado razonable gracias a casi copiar el planteamiento de Python.

Sin embargo las metodologías de programación es una temática larga y miserablemente olvidada dentro de los planes de estudios de las carreras de Ingeniería así que no nos queda más remedio que dejar un enorme hueco en este curso.

Me centraré en comentar lo más básico y fundamental de lo que es un objeto: los atributos y los métodos. De este modo veremos una clase como una manera de agrupar variables, los atributos, y funciones que operan sobre estas variables, los métodos.

Es imposible hablar de Python y no hablar sobre la orientación a objetos porque en Python prácticamente todo es un objeto. Por ejemplo un número complejo es un ejemplo especialmente simple.

>>> c = 2+3j>>> print c,type(c)(2+3j) <type 'complex'>>>> c.real2.0>>> c.imag3.0>>> print c*(1j)+32j

Python dispone de una constante especial, j que es la unidad imaginaria. Como en Matlab y Octave es recomendable utilizarlo como sufijo de un número tal como se hace en el ejemplo. Cualquier número imaginario tiene dos atributos, su parte real y su parte imaginaria.

Si bien la suma de un número complejo es una operación trivial (es la suma de sus partes real e imaginaria respectivamente) la multiplicación no lo es. Esto significa que la clase complex tiene la operación de producto definida internamente. Podemos ver todos los atributos, métodos y operaciones disponibles para una clase utilizando la función help.

>>> help(c)Help on complex object:

class complex(object) | complex(real[, imag]) -> complex number | | Create a complex number from a real part and an optional imaginary part. | This is equivalent to (real + imag*1j) where imag defaults to 0. | | Methods defined here: | | __abs__(...) | x.__abs__() <==> abs(x) | | __add__(...) | x.__add__(y) <==> x+y | | __coerce__(...) | x.__coerce__(y) <==> coerce(x, y)

Page 9: Introduccion a Python Para Ingenieros

(...)

Esta función que aparece como __abs__() es en realidad la función valor absoluto, de modo que estas dos operaciones:

>>> abs(c)3.605551275463989>>> c.__abs__()3.605551275463989

Son equivalentes a todos los efectos.

En Python todo está modularizadoEsta sí es una diferencia esencial entre Matlab/Octave y Python. En estos lenguajes cualquier función de la biblioteca está accesible al intérprete. Esto hace que, a medida que el número de funciones crece, crezca también la probabilidad de conflictos.

En Python todas las bibliotecas, incluso la biblioteca estándar, están modularizadas. Por ejemplo, si queremos calcular el seno de tendremos que importar antes el módulo que contiene tanto la función seno como el valor de

>>> import math>>> math.sin(math.pi)1.2246063538223773e-16

Dos puntos a tener en cuenta:

• Cada módulo es en sí un objeto. En este caso, después de importar math, hemos llegado a la constante como un atributo del módulo y a la función sin como un método.

• Prácticamente la totalidad de módulos o scripts en Python importan algún módulo. Podemos importar módulos prácticamente en cualquier punto de la ejecución pero por convención se suelen importar al principio.

Ahora podéis pensar que para la función seno o para :math:pi, tener que arrastrar el nombre math puede ser algo tedioso; especialmente si no hay una intención especial de agrupar las funciones de este módulo. Si queremos importar sólo sin y pi podemos hacerlo de la siguiente manera:

>>> from math import sin,pi>>> sin(pi)1.2246063538223773e-16

También podéis pensar... ¿Y si tengo que importar veinticinco funciones del módulo math? ¿Tengo que escribirlas todas en la llamada a import? Evidentemente no. Podemos utilizar un wildcard para importar todo el contenido del módulo y ponerlo a disposición del programa:

>>> from math import *>>> sin(pi)1.2246063538223773e-16>>> cos(pi)-1.0>>> tan(pi)-1.2246063538223773e-16

Aunque esta manera de importar el contenido de los módulos es bastante práctica porque evita olvidos no es la recomendada para producción.

Page 10: Introduccion a Python Para Ingenieros

Python incluye baterías, pero no cargadorEn la introducción, porque siempre es mala idea no leer la introducción, mencioné que para programar en Python es una gran idea acostumbrarse a utilizar un interfaz de desarrollo integrada (IDE) como Eclipse; algo más sofisticado que IDLE.

Cuando se dice que Python incluye baterías se menciona el hecho que la biblioteca estándar es enorme comparada con otros lenguajes de programación, que sólo incluye funcionalidades básicas. La biblioteca estándar de Python incluso viene con la posibilidad de generar interfaces gráficas con ventanas en cualquier sistema operativo.

Pero Python no es Matlab ni Visual Basic en el sentido que uno debe decidir cómo programará, gestionará y ejecutará sus scripts o módulos. Es más, debido a que Python tiene la gran particularidad de que el significado de un programa depende de cómo se ha escrito es prácticamente imprescindible utilizar una herramienta específica.

A modo de ejemplo ejecutaremos un “Hola, mundo!” portable, es decir, podemos seguir exactamente el mismo método en cualquier sistema operativo.

Una vez abrimos IDLE, en el menú archivo seleccionamos nueva ventana, lo que abrirá un editor en el que podemos escribir el programa. Entonces en esta nueva ventana escribimos lo siguiente:

if __name__ == '__main__': print 'Hola, Mundo!'

Editor para Python de IDLE en Linux

Justo después de escribir los dos puntos finales de la primera línea veremos que el editor nos sitúa automáticamente a cuatro caracteres del margen izquierdo. El motivo puede parecer puramente estético pero leed otra vez el programa. Hay un condicional, un if, y ninguna sentencia que termine el bloque. No hay ningún end ni corchetes que encapsulen las sentencias ejecutables.

Lo que determina la prioridad del bloque de código es precisamente la separación respecto al margen izquierdo. Todo lo que esté indentado después de los dos puntos es parte del bloque if. La necesidad de utilizar una herramienta específica radica aumentar la facilidad en la que se maneja la indentación del código. En IDLE, por ejemplo, para cambiarla basta con apretar el tabulador o backspace al principio de cada línea para cambiarla.

Pero si comparamos IDLE con el IDE de Matlab seguimos echando de menos un montón de piezas: la ayuda integrada, algo que nos permita navegar entre los objetos, un debugger, un profiler... Parte de la gracia de cualquier lenguaje de programación, y es también el caso de C o Fortran, es llegar a un entorno de desarrollo con el que nos sintamos cómodos. La comodidad es una sensación muy personal y para conseguirla puedo ayudaros muy poco.

Page 11: Introduccion a Python Para Ingenieros

Ahora, en la ventana del editor, seleccionad run y luego run module o pulsad F5. En el intérprete aparecerá un Hola, Mundo!.

La parte inicial, el if __name__ == '__main__': es una convención de Python que viene a decir que lo que hay a partir de esta línea tiene que ejecutarse si se ejecuta el archivo .py. Lo utilizaremos otras veces y veremos de su importancia más adelante.

Python es también una calculadoraEl intérprete cuenta con todas las operaciones aritméticas usuales: suma, resta, multiplicación, división...

Sólo hay que hacer un par de puntualizaciones al comportamiento del lenguaje. El símbolo correspondiente a la potencia es el doble asterisco, **, como en Fortran.

>>> 2**101024

Otra diferencia es el operador modulo que da el residuo de la división entrera entre dos números. En Matlab, Octave y Fortran este operador es una función, a diferencia de C en el que se trata de un operador. Python comparte la convención con C al respecto

>>> 5%21

La división tiene un comportamiento un poco particular en Python 2 y depende del tipo de cada operador. Si tanto el numerador como el denominador son números enteros, el operador / corresponde a la división entera y no a la división en coma flotante. Sin embargo, si alguno de los dos operandos es un número en coma flotante el resultado también lo será.

>>> 5/22>>> 5.0/22.5

Sin embargo este comportamiento se corregirá en Python 3 de manera que cualquier división será la división en coma flotante. Podemos modificar el comportamiento de Python 2 utilizando el módulo __future__ que introduce algunas de las modificaciones que recibirá el lenguaje en el futuro

>>> from __future__ import division>>> 5/22.5

Por lo demás el comportamiento respecto a las operaciones aritméticas es el mismo e importando los módulos math y cmath conseguiremos funcionalidades equivalentes a cualquier calculadora. Aunque aún estamos muy lejos de algo parecido a Matlab y Octave.

Page 12: Introduccion a Python Para Ingenieros

Control de flujoEste capítulo no es más que una introducción sobre cómo hacer lo que se puede hacer con cualquier lenguaje de programación en Python. El control de flujo es el nombre técnico de las estructuras condicionales, los bucles y sus derivados mientras que el encapsulamiento es cómo se llama a la técnica de tomar partes de nuestro código y convertirlas en funciones.

Quiero enfatizar que esta parte, en el fondo, no tiene nada que ver con Python. Si uno experimenta dificultades para seguir esta sección: náuseas, mareos o desorientación espacial; recomiendo encarecidamente tomar un curso de programación de verdad ya sea en la Universidad o en Youtube. Ahora incluso los hay buenos.

Condicionales o sentencias ifEl condicional más sencillo posible es el siguiente:

>>> if True: print 'Verdadero'...Verdadero

Dos cosas interesantes aquí. Hay una constante especial para determinar verdadero, True, del mismo modo que lo hay para falso con False. Aunque se cumple la regla que se considera como falso el 0 entero y como verdadero cualquier otro valor se enfatiza como buena práctica que cualquier condicional debe evaluar una constante lógica que puede tener como valor True o False.

Otra peculiaridad es que no tenemos por qué pasar a la siguiente línea si después de la condición y los dos puntos sólo queremos escribir una línea. De este modo estas dos sentencias son equivalentes a

>>> if True:... print 'Verdadero'...Verdadero

El intérprete, en cuanto se da cuenta que hemos escrito dos puntos, nos cambia el símbolo de entrada a tres puntos. Esto significa que está esperando nuestra decisión de si queremos introducir contenido en el bloque o queremos salir de él.

Para complicar un poco más el control de flujo pasaremos de trabajar con la consola a trabajar con scripts. Recordad que cualquier archivo con código escrito en Python tendrá la extensión .py. Por ejemplo:

from random import choice

if __name__ == '__main__': guess = choice([True,False]) if guess: print 'Cara' else: print 'Cruz'

Vemos que la condición lógica complementaria también requiere de un bloque de código asociado después de los dos puntos de rigor.

Vemos también varias cosas nuevas e interesantes en este programa. Uno es el módulo random que contiene multitud de funciones específicas para la generación de números o secuencias aleatorias de

Page 13: Introduccion a Python Para Ingenieros

cualquier tipo. En este caso hemos importado la función choice, que dada una lista (que aún no sabéis lo que es) escoge un elemento de la misma de manera aleatoria. De este modo, si ejecutáis el script varias veces, algunas veces os saldrá Cara y otras veces Cruz.

Python 2.7 (r27:82500, Aug 07 2010, 16:54:59) [GCC] on linux2Type "copyright", "credits" or "license()" for more information.>>> ================================ RESTART ================================>>>Cruz>>> ================================ RESTART ================================>>>Cruz>>> ================================ RESTART ================================>>>Cruz>>> ================================ RESTART ================================>>>Cruz>>> ================================ RESTART ================================>>>Cara

Aunque alguien avispado me podría decir que este programa se podía escribir de manera mucho más eficiente ahorrándome el condicional

from random import choice

if __name__ == '__main__': print choice(['Cara','Cruz'])

Obviamente podemos introducir condiciones adicionales a la estructura con elif y complicar un poco la estructura:

from random import choice

if __name__ == '__main__': (pos,nil,neg) = (0,0,0) # Multiple assignment while pos<10 and nil<10 and neg<10: num = choice([-1,0,1]) if num == 0: nil += 1 elif num > 0: pos += 1 elif num < 0: neg += 1 else: print 'Something impossible just happened!'

print 'Positive:',pos,'Zero:',nil,'Negative:',neg

Lo que estamos haciendo con este script es tomar tres variables, pos, nil y neg que utilizaremos como contadores. La función choice escoge entre tres valores y dependiendo de si su valor es negativo, positivo o cero incrementa el contador correspondiente. En este caso la condición complementaria else no tiene ningúna función porque las tres condiciones anteriores cubren todo el espacio de probabilidades pero lo he dejado ahí por si alguien tiene un ordenador que con una lógica alternativa.

Como aún no hemos visto prácticamente nada de Python cualquier tontería nos parece novedosa. Hay muchas cosas interesantes en este pequeño ejemplo

• Una asignación múltiple en la primera línea

Page 14: Introduccion a Python Para Ingenieros

• Un bucle con condicional while en el que hemos puesto una condición lógica compuesta • El operador incremento += presente en prácticamente todos los lenguajes de programación

excepto Matlab y Fortran.

Ejercicio 1

La sucesión de Fibonacci tiene la siguiente definición:

Escribir un programa que saque por pantalla los 20 primeros términos de la sucesión de Fibonacci.

Ejercicio 2

Aunque los bombos y las esferas de plástico no tienen porqué ser imparciales se prefieren a los ordenadores para las loterías y los bingos. También es verdad que los generadores de números pseudoaleatorios tampoco son perfectos.

Escribir un programa que sortee la primitiva, cinco extracciones de un conjunto de 49 números sin repetición. Merece la pena echarle un vistazo a la ayuda del módulo random.

Intermezzo. Listas.Aunque esta sección debería estar en el próximo capítulo, el destinado a los distintos tipos que Python proporciona, es importante conocer lo básico sobre las secuencias (la lista es un tipo de secuencia) para entender cómo funcionan los bucles.

A diferencia de C o Fortran, en el que existen verdaderos bucles con el for de C o el do de Fortran, en Python (al igual que en Matlab) no existen los bucles como tal. Lo que tenemos son iteradores en el que asignamos a una variable el elemento siguiente de algo sobre lo que podamos iterar.

La secuencia más común en Python es la lista, un tipo que sería algo entre una matriz y una celda en Matlab. El literal se introduce mediante corchetes

>>> a = [1,2,3,4]>>> print a[1, 2, 3, 4]

Una lista en Python es una lista de “cosas”, de modo que puede contener absolutamente cualquier valor independientemente de su tipo.

>>> a = [1,'hola',True]>>> print a[1, 'hola', True]

Las listas están indexadas, como cualquier cosa en Python, con la numeración a partir de cero:

>>> print a[0]1

Una de las propiedades más curiosas y útiles a la vez de la indexación en Python es la posibilidad de utilizar índices negativos para numerar una secuencia desde el final. El elemento correspondiente al índice -1 será el último elemento de la secuencia

>>> print a[-1]True

Page 15: Introduccion a Python Para Ingenieros

Las listas son secuencias mutables, es decir que podemos cambiar cualqiera de sus elementos por asignación

>>> a[-1] = False>>> print a[1, 'hola', True]

Si después de estos comandos pedís la documentación de cualquier lista, en este caso con help(a) comprobaréis que disponemos de un montón de métodos para manipular tanto sus elementos como el orden de los mismos.

Veremos más sobre las listas en el siguiente capítulo. De momento ya sabemos lo suficiente para entender el concepto de iterador.

IteradoresQuizás la función más básica que genera una lista de valores es la función range:

>>> print range(5)[0, 1, 2, 3, 4]

Vemos que se trata de una lista de valores desde cero con incremento 1 de 5 elementos. Esta función nos permite generar un iterador tan básico como nos es posible

>>> for i in range(5)... print i,...0 1 2 3 4

Entenderemos perfectamente la diferencia entre un iterador y un bucle con el siguiente ejemplo

>>> for i in range(5)... i = i**2... print i,0 1 4 9 16

Vemos que aunque hemos modificado la variable i dentro del bloque correspondiente a una iteración, al entrar en la siguiente iteración, i se ha sobreescrito con el siguiente elemento de la secuencia. Es importante tener este comportamiento en cuenta porque implica que algunos algoritmos tal como estan publicados en C o en Fortran no pueden ser simplemente copiados en Python.

Podemos controlar los bucles con las sentencias break y continue, por ejemplo

>>> for w in ['defenestrate','the','cat','now']:... if w == 'cat':... print 'No, I love cats!'... break... else:... print w...defenestratetheNo, I love cats!

En el siguiente ejemplo representamos por pantalla sólo los números impares de la secuencia entre 0 y 9, aunque iteramos con la secuencia entera.

>>> for num in range(10):... if num%2 == 0:

Page 16: Introduccion a Python Para Ingenieros

... continue

... else:

... print num,1 3 5 7 9

Esta no es ni mucho menos la manera de tratar los bucles en Python. Los bucles en cualquier lenguaje interactivo son lentos comparados con otros lenguajes como C o Fortran, más orientados a obtener un buen rendimiento. Python no es una excepción de modo que debemos evitar iterar sobre secuencias muy largas innecesariamente. Por ejemplo, si queremos iterar sobre sólo los valores impares de una secuencia de números podemos filtrarla antes, de este modo nos aseguraremos que el iterador realiza sólo los ciclos imprescindibles.

>>> from itertools import ifilter>>> for num in ifilter(lambda x: x%2, range(10)):... print num,1 3 5 7 9

Para entender el ejemplo anterior necesitamos saber qué son las funciones lambda. Llegaremos a ello poco después de entender cómo Python reinterpreta el concepto de función.

El módulo itertools es uno de estos secretos escondidos de Python que uno siempre se arrepiente de no haber conocido antes. Alguna de las utilidades de este módulo, al igual que algunos de los trucos que provienen de la programación funcional, ahorran muchas líneas de código y aceleran el resultado significativamente.

Importante

Uno de los problemas de obligar a formatear el código de una determinada manera es el no poder dejar bloques vacíos. Por ejemplo:

>>> while True:... # Implement this later

^IndentationError: expected an indented block

Vemos que, aunque hemos puesto el comentario precisamente donde debíamos se queja que no es capaz de entender el bloque de código. Esto es precisamente porque los comentarios no sirven para marcar un bloque de código; son comentarios, no código.

Para dejar un bloque vacío contamos con la sentencia pass

>>> while True:... pass # Implement this later

De este modo ya no generamos ningún error

Page 17: Introduccion a Python Para Ingenieros

Definición de funciones, encapsulamiento y módulosAunque Python es estrictamente un lenguaje orientado a objetos tiene todas las piezas necesarias para utilizar el paradigma procedimental, también llamado “código spaghetti con funciones para que quien lo lea no se atragante”.

Python, al igual que Matlab, no diferencia entre funciones y subrutinas pero al igual que fortran pasa siempre los argumentos por referencia. Esto significa que si cambiamos alguno de los argumentos de entrada lo estaremos cambiando de verdad, no una hipotética copia hecha en tiempo de ejecución, así que cuidadín.

La función más sencilla que podemos definir es es la función vacía sin argumentos:

>>> def none():... pass

Es una función que no recibe ningún argumento, no hace nada y no devuelve ningún argumento de salida.

Duck TypingConstruyamos una función un poco más complicada.

>>> from math import sqrt>>> def rms(a):... return sqrt(a**2.mean())

En esta vemos todas las piezas necesarias para definir una función de verdad. Recibe un argumento, a, realiza un cálculo con este mismo argumento y devuelve un resultado a la salida. Ahora intentmos llamarla desde el intérprete:

>>> rms([1,2,3,4])Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in rmsTypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

Este ejemplo nos sirve para introducir el concepto de duck typing. Cuando hemos definido la función func:rms y hemos definido el argumento a no hemos especificado ningún tipo. Declarar los argumentos de entrada y de salida es algo obligatorio en, por ejemplo, Fortran y C. En cambio en Matlab no es necesario porque la mayoría de las variables tienen el mismo tipo, una matriz. En cambio Python es un lenguaje orientado a objetos así que, estrictamente hablando, tiene infinitos tipos posibles. Sin embargo al definir la función no se nos pide que declaremos de qué tipo es. Encima en el cuerpo de la función asumimos que el argumento de entrada ‘a’ se puede elevar al cuadrado y dispone del método mean(). ¡Y nadie nos avisa de lo mucho que podemos cagarla con esto!

Este es el concepto de duck typing. Uno puede hacer lo que le dé la real gana dentro de cualquier bloque de función, o en un método, y es responsable de lo que haya dentro. Y si da un error es culpa del programador. Este comportamiento no implica que Python sea un lenguaje menos formal. Si en un lenguaje donde tenemos que declarar el tipo de todos los argumentos nos equivocamos llamando una función nos avisará en tiempo de compilación. Pero como Python es un lenguaje interpretado la decisión ha sido dejar que estos errores simplemente sucedan en tiempo de ejecución.

Page 18: Introduccion a Python Para Ingenieros

En el caso de la función anterior nosotros, como programadores, definimos la función rms(), por Root Mean Square, por la raíz cuadrada de cualquier tipo que pueda elevarse al cuadrado y disponga del método mean().

Afortunadamente, por si nos equivocamos y pasamos a rms() algo que no cuadra, el sistema de excepciones de Python es excelente. Quizás el mejor que uno pueda encontrar. Si nos fijamos en el mensaje de error vemos que es perfectamente explicativo: no puedo elevar al cuadrado una lista de enteros. Entonces llegamos a la conclusión que la función func:rms está diseñada para funcionar con un tipo que no es la lista y que dispone del método mean(), como por ejemplo un array.

>>> from numpy import array>>> rms(array([1,2,3,4,5,6],dtype='double')3.8944404818493075

Nota

Acabamos de utilizar la clase array del módulo numpy. Es una clase esencial para el Cálculo Numérico en Python así que le dedicaremos un capítulo entero más adelante.

DocstringsSi no hay pistas sobre los tipos en las cabeceras tendremos que documentar convenientemente cada función. Lo más sencillo es utilizar una cadena de texto con soporte para salto de línea justo después de la definición de la cabecera

from math import sqrt

def rms(a): """ Computes the root mean square of *a*, which is a numpy array. The result is a double constant. """ return sqrt((a**2).mean())

Ahora utilizamos la función help() para ver qué pinta tiene en la consola

>>> help(rms)Help on function rms in module func1:

rms(a) Computes the root mean square of *a*, which is a numpy array. The result is a double constant.

Python disponde de excelentes herramientas para tratar la documentación incluida en el código. Disponemos de herramientas capaces de crear manuales a partir de dicha documentación, formatos propios para redactarla e incluso podemos introducir notación matemática en LaTeX para describir algoritmos.

La herramienta en la que está redactado esta documentación, Sphinx, es de gran ayuda cuando cualquier proyecto empieza a crecer de verdad para mantener todo bien ordenado y documentado.

MódulosPodemos utilizar los módulos para ordenar las funciones que vayamos escribiendo de manera eficaz. Tomemos como ejemplo el siguiente archivo llamado means.py que contiene la definición de varias medias posibles:

from math import sqrt

Page 19: Introduccion a Python Para Ingenieros

def rms(a): """ Computes the root mean square of *a*, which is a numpy array. The result is a double constant. """ return sqrt((a**2).mean())

def cmc(a): """ Computes the cubic root mean cube of *a*, which is a numpy array. The result is a double constant. """ return ((a**3).mean())**(1.0/3.0)

def nmn(a,n): """ Computes the nth root mean nth power of *a*, which is a numpy array. The result is a double constant. """ return ((a**n).mean())**(1/float(n))

Quizás la manera más efectiva de gestionar colecciones de funciones es la siguiente:

>>> import means>>> help(means)Help on module means:

NAME means

FILE /home/guillem/intropy/_static/means.py

FUNCTIONS cmc(a) Computes the cubic root mean cube of *a*, which is a numpy array. The result is a double constant.

nmn(a, n) Computes the nth root mean nth power of *a*, which is a numpy array. The result is a double constant.

rms(a) Computes the root mean square of *a*, which is a numpy array. The result is a double constant.

sqrt(...) sqrt(x)

Return the square root of x.

Esto implica que podemos utilizar el nombre del módulo, means, como prefijo del espacio de nombres:

>>> from numpy import array>>> means.cmc(array([1,2,3,4,5,6],dtype="double"))4.1888593641200274

Y todo esto gratis sólo por haber ordenado las distintas funciones dentro del mismo archivo.

Page 20: Introduccion a Python Para Ingenieros

Intermezzo. TuplesYa concemos un tipo básico de secuencia, la lista. Es el momento de conocer otro: el tuple.

Podemos hacernos una idea intuitiva de qué es si analizamos la asignación múltiple. Cuando escribo lo siguiente:

>>> a = 2

Asigno el literal número entero 2 a la variable a. Podemos complicar un poco la asignación haciendo dos a la vez

>>> a,b = 1,2

Acabo de realizar una asignación múltiple. En este caso la variable a contendrá el número entero 1 y la variable b el número entero 2. Podemos utilizar también la siguiente sintaxis:

>>> (a,b) = (1,2)

A todos los efectos esta sentencia ejecutable es idéntica a la anterior. La única diferencia es que en la anterior hemos utilizado la sintaxis reservada a las asignaciones múltiples mientras que en este caso hemos utilizado una variable doble. Y es precisamente este concepto de variable múltiple el que nos sirve para entender el tuple.

El tuple es una secuencia inmutable que se puede utilizar, a todos los efectos, como una secuencia de variables de longitud arbitraria.

El hecho que no sea inmutable diferencia el tuple de la lista. Sólo podemos cambiar el tuple, no podemos cambiar ninguno de sus elementos independientemente. Como demostración:

>>> a = (1,2,False)>>> a[2] = TrueTraceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'tuple' object does not support item assignment

Importante

Un tuple no sirve para lo mismo que una lista. Un tuple sirve para concatenar variables sin que haya una relación formal entre ellas. Las listas son para conjuntos ordenados de elementos que, tengan o no el mismo tipo, estan relacionados conceptualmente.

Los tuples se indexan igual que las listas, utilizando corchetes. No sólo no se pueden cambiar los elementos de un tuple sino que tampoco se pueden sustraer o añadir más elementos.

Funciones que retornan varias variablesGracias al concepto de tuple una función que retorna varias variables no es más que una función que retorna un tuple

>>> def something(a):... return (a,a**2,a**3)...>>> a,squarea,cubea = something(3)>>> print cubea27>>> (a,squarea,cubea) = something(2)>>> print cubea8>>> a = something(4)>>> print a

Page 21: Introduccion a Python Para Ingenieros

(4, 16, 64)

Funciones con argumentos por omisiónPodemos definir argumentos opcionales con un valor por omisión con la siguiente notación

>>> def something(a,b=2)... return(a,a**b,a**(b+1))...>>> a,squarea,cubea = something(3)>>> print cubea27>>> a,squarea,cubea = something(3,1)>>> print cubea9

Empaquetar y desempaquetar argumentosPodemos empaquetar argumentos de una función utilizando un tuple y el operador asterisco. Por ejemplo, si una función debe recibir dos argumentos podemos pasarle un único argumento doble y desempaquetarlo con el asterisco

>>> a,squarea,cubea = something(*(3,1))... print cubea9

Podemos también desempaquetar argumentos con el operador doble asterisco (**) y un diccionario pero como no sabemos qué es un diccionario vamos a por otro intermezzo.

Intermezzo. Diccionarios.Hemos visto ya dos tipos distintos de tipos derivados: las listas y los tuples. Los primeros sirven para almacenar conjuntos ordenados de valores en los que el orden cuenta y los segundos sirven para juntar valores de manera inmutable.

En la programación moderna se ha popularizado una manera de almacenar datos de tipo key-value. En las listas y los tuples podemos acceder a cada valor a través de un índice, en el caso de Python numerado desde el cero. El índice es, de manera natural, un número entero. Esta no es la manera necesariamente más adecuada para nombrar un valor, por ejemplo cuando una lista contiene números enteros y podemos confundir fácilmente el índice con el contenido.

El almacenamiento key-value asigna una clave a cada valor que contiene el tipo derivado, de manera que forman una pareja. La impliementación de esta estrategia en Python es el diccionario.

>>> d = {1 : 1, 'dos': 2, 3: [1,2,3]}>>> print d{1: 1, 3: [1, 2, 3], 'dos': 2}>>> d['cuatro'] = 2.443>>> print d{'cuatro': 2.443, 1: 1, 3: [1, 2, 3], 'dos': 2}>>> for key in d.iterkeys():... print d[key]...2.4431[1, 2, 3]2

Page 22: Introduccion a Python Para Ingenieros

Vemos que los diccionarios son mutables, podemos eliminar o añadir más parejas clave-valor. La manera de acceder a un valor a través de la clave es la misma que utilizaríamos si fuera el índice de una lista o un tuple, con la diferencia que este no es necesariamente un entero.

El ejemplo anterior no es muy bueno porque da un rodeo por las claves para iterar sobre los valores. Una manera un poco más efectiva de hacerlo es utilizando el método itervalues()

>>> for value in d.itervalues():... print value...2.4431[1, 2, 3]2

Advertencia

En los diccionarios, como en cualquier almacenamiento key-value, el orden en el que realmente están almacenadas las parejas no se no se considera relevante. Si creamos un diccionario introduciendo las parejas en un orden dado puede ser que Python lo cambie por cualquier motivo que se puede escapar a nuestra comprensión.

De vuelta al empaquetado y desempaquetado de argumentosAhora que ya sabemos lo básico sobre los diccionarios podemos volver al tema que traíamos entre manos. El problema de pasar argumentos con el tuple y el operador * es que quizás no queremos introducir todos los argumentos en una llamada donde algunos de los argumentos tienen un valor por omisión.

Os pongo un ejemplo

>>> def dummy(a,b=2,c=3,d=4):... print a,c... print b,d...>>> dummy(1)1 32 4

¿Cómo conseguimos llamar la función anterior sólo mencionando los agumentos a y d? Pues con los diccionarios y el operador ** es tan sencillo como lo siguiente

>>> dummy(1,**{'d':123})1 32 123>>> dummy(**{'a':321,'d':123})321 32 123

Nota

En la función anterior el argumento a es imprescindible. Sea cual sea la manera en la que escojamos introducir ese argumento no nos lo podemos olvidar o recibiremos un error.

Funciones lambdaUna función lambda es una estructura que en tiempo de ejecución convierte una variable en una función con argumentos. Aunque algunos habrán visto este tipo de estructuras por primera vez en Matlab con los function handles, expresados por el símbolo @, las funciones lambda son un invento

Page 23: Introduccion a Python Para Ingenieros

de los años cincuenta.

De hecho son un invento del primer lenguaje interpretado de la historia, lisp que es casi contemporáneo a Fortran y fue el primer lenguaje que utilizaba el paradigma funcional. Lisp tuvo un gran arranque y se convirtió rápidamente en el lenguaje por excelencia en la investigación en inteligencia artificial pero como nunca se enseñó demasiado en las universidades cayó un poco en el olvido.

El paradigma funcional sigue vivo en algunos lenguajes de programación modernos como Scheme o Haskell, incluso Python adopta algunas de las herramientas propias de la programación funcional.

La estructura de una función lambda en Python conserva la forma que tenía en lisp:

>>> square = lambda x: x*x>>> print square(3)9

Si necesitamos más de una variable

>>> prod = lambda x,y: x*y>>> print prod(3,4)12

Las funciones lambda son muy útiles para definir funciones cortas que sólo usaremos una vez.

Programación funcionalPython no sigue el paradigma funcional pero reinterpreta algunas de sus características más útiles como son las funciones map() y reduce()

La primera aplica una función dada a todos los elementos de una lista o secuencia y nos devuelve una lista con el resultado

>>> print map(lambda x:x[0]*x[1],[(1,2),(2,3),(3,4),(4,5)])[2, 6, 12, 20]

O podemos empezar a utilizar los módulos de la librería estándar para empezar a contestarnos preguntas que siempre quisimos formular. ¿Cuánto suman cada una de las combinaciones con repetición de dos elementos de los números del 1 al 4?

Page 24: Introduccion a Python Para Ingenieros

Tipos, o cómo programar en PythonEn Python todos los tipos intrínsecos son objetos y como tales tienen atributos y métodos. Un viaje por los tipos intrínsecos de Python es, en el caso de no haber utilizado nunca la orientación a objetos, una demostración práctica sobre cómo se usan objetos definidos por terceros. De hecho, estrictamente hablando, definir nuevos tipos en Python es utilizar un conjunto bastante limitado de las funcionalidades de la orientación a objetos.

Ya hemos hablado de los números enteros, reales y complejos; de las listas, los tuples y los diccionarios; sobre los que volveremos a hablar en este capítulo. Nos faltan las cadenas de caracteres.

La clase stringAunque las cadenas de caracteres no son un tipo demasiado importante en el Cálculo son la sustancia de multitud de algoritmos. Sólo veremos lo más básico de esta clase que, en muchos sentidos, no es más que una cadena de caracteres.

Nota

Una diferencia importante entre Python 2 y Python 3 es que en la en el primero existen diferencias entre las codificaciones de caracteres. No es lo mismo un caracter ASCII de uno latin1 o de uno UTF-8. La codificación es una caraceterística propia de algunos tipos de cadenas de caracteres. Esto cambia en Python 3, todas las cadenas de caracteres son Unicode, en este caso UTF-8. Esto implica que incluso los archivos de código en Python serán unicode y que uno puede utilizar tildes y caracteres localizados para escribir comentarios.

El soporte para Unicode es esencial en lo que respecta a internacionalización y nos incumbe a nosotros puesto que no siempre querremos utilizar inglés para todo.

Para introducir una cadena de caracteres podemos utilizar bien comillas simples o comillas dobles.

>>> a = 'string'>>> b = 'string'

o bien podemos crear cadenas de caracteres vacías

>>> c = ''>>> d = str()

Las cadenas de caracteres, como hemos visto anteriormente, soportan algunas operaciones aritméticas como la suma y la multiplicación; obviamente reinterpretadas como secuencia de caracteres.

Una de las operaciones más habituales con caracteres es la de reconocer secuencias. Para ello contamos con la función find() y la sentencia in.

>>> 'r' in 'string'True>>> str('string').find('r')2

Uno de los pocos motivos por el que queremos un string en Cálculo Numérico es para manipular archivos que contienen datos y sus nombres. Por ejemplo, numerarlos:

>>> for i in range(10):... 'datafile'+str(i).zfill(3)+'.dat''datafile000.dat'

Page 25: Introduccion a Python Para Ingenieros

'datafile001.dat''datafile002.dat''datafile003.dat''datafile004.dat''datafile005.dat''datafile006.dat''datafile007.dat''datafile008.dat''datafile009.dat'

Importante

Este es un buen ejemplo para introducir el concepto de pythonic. En Python, como en cualquier lenguaje de programación, hay infinitas maneras de implementar algo. Especialmente si el lenguaje es muy extenso y es poco estricto en lo que a sintaxis se refiere. Este es precisamente el caso de Python.

En el ejemplo anterior hemos creado una cadena de caracteres utilizando el operador aritmético suma para concatenar caracteres. Aunque esto sea perfectamente correcto y leíble no es el modo más pythonic de llegar al objetivo. Es unpythonic

Este adjetivo se suele utilizar para implementaciones que, en vez de utilizar alguna funcionalidad lateral del lenguaje, como por ejemplo el comportamiento de un operador aritmético cuando se aplica a cadenas de caracteres, utiliza un método más leíble y mejor documentado.

Esta distinción es completamente subjetiva y sólo puede utilizarse cuando ya se cuenta con una sólida experiencia programando en Python.

Un ejemplo de lo que es o no pythonic es optar por utilizar de manera casi obsesiva los métodos de los tipos más usuales como str, list o dict. Estas clases están implementadas enteramente en C y utilizar sus métodos suele ser más eficiente que implementar el algoritmo nosotros mismos.

Podemos proponer una implementación más convencional de lo anterior utilizando el método join() de la clase str

>>> for i in range(10):... str().join(['datafile',str(i).zfill(3),'.dat'])...'datafile000.dat''datafile001.dat''datafile002.dat''datafile003.dat''datafile004.dat''datafile005.dat''datafile006.dat''datafile007.dat''datafile008.dat''datafile009.dat'

Otra posibilidad bastante útil es la de completar cadenas de caracteres dando formato a sus argumentos al igual que hacemos con el comando print en C. Por ejemplo:

>>> for i in range(10):... 'datafile%03i.dat'%(i)...'datafile000.dat''datafile001.dat'(...)

Lo que viene después del símbolo de porcentaje es un tuple en el que podemos alinear todos los argumentos que tenga la cadena de caracteres. Aunque, otra vez, hay una manera mucho más

Page 26: Introduccion a Python Para Ingenieros

pythonic de hacer exactamente lo mismo, mediante la función format() de cualquier cadena de caracteres

>>> for i in range(10):... 'datafile{:03d}.dat'.format(i)...'datafile000.dat''datafile001.dat'(...)

Truco

Hay un buen tutorial sobre la manera de escribir cadenas de caracteres con formato en la documentación estándar del lenguaje

Empezamos a ver que si buceamos un poco por entre la documentación de Python podemos llegar a escribir código perfectamente leíble, eficiente y bonito.

>>> from random import choice>>> for i in range(10):... 'Lanzamiento {}, me ha salido {}'.format(i,choice(['Cara','Cruz']))...'Lanzamiento 0, me ha salido Cruz''Lanzamiento 1, me ha salido Cara''Lanzamiento 2, me ha salido Cruz''Lanzamiento 3, me ha salido Cara''Lanzamiento 4, me ha salido Cara''Lanzamiento 5, me ha salido Cruz''Lanzamiento 6, me ha salido Cruz''Lanzamiento 7, me ha salido Cara''Lanzamiento 8, me ha salido Cara''Lanzamiento 9, me ha salido Cruz'

Esto nos llevaría un buen rato en cualquier otro leguaje que no fuera Python, incluso en Matlab.

Literal

Hay múltiples maneras de definir una cadena de caracteres directamente sin necesidad de hacer una llamada a la clase str. Podemos utilizar bien las comillas simples o las comillas dobles indistintamente o cuando necesitemos alguno de los dos caracteres dentro. Por ejemplo, si necesitamos una comilla simple dentro de una cadena de caracteres:

>>> print "I'm afraid that was the funniest practical joke"I'm afraid that was the funniest practical joke

O viceversa, si necesitamos algunas comillas dobles podemos introducir los caracteres entre comillas simples.

También disponemos del control de carro con los caracteres especiales usuales como

>>> print "I am a whale!\n'______'"I am a whale!'______'

Pero si lo que realmente queremos es introducir una cadena de caracteres con más de una línea tenemos un literal específico para ello

>>> print """I have seen {}... elephants hanging... on a spider web""".format('MILLIONS!!!')I have seen MILLIONS!!!

Page 27: Introduccion a Python Para Ingenieros

elephants hangingon a spider web

También en este caso podemos utilizar comillas simples o dobles.

Intermezzo. Archivos y la clase fileEn Cálculo Numérico utilizamos esencialmente números. Las cadenas de caracteres nos sirven para poder expresar texto, normalmente datos. Estos datos suelen terminar en archivos que contienen, oh sorpresa, caracteres.

El problema es que de momento no tenemos ni idea de cómo abrir, leer, escribir y cerrar un archivo. Para ello necesitamos conocer la clase file que casi siempre instanciaremos a partir de la función open() de la librería estándar.

Nota

Soy un usuario de Linux desde hace ya un montón de años. Algunos de los ejemplos de este libro rezuman cultura UNIX por todos los poros y uno puede pensar que Python es un juguetito de los que utilizamos esta familia de sistemas operativos.

Esto no es verdad en absoluto. Los desarrolladores de Python han hecho un importante esfuerzo para abstraer prácticamente cualquier función del sistema operativo en el que estemos trabajando. Muchas de las utilidades para no tener que depender del SO están en los módulos os, sys y shutil. Aunque no hemos hablado sobre la librería estándar empezaré a utilizarlos aquí para que los ejemplos funcionen en cualquier sistema operativo.

Importante

La manera usual de ejecutar Python en sistemas UNIX es llamarlo desde una consola. Esta manera de funcionar tiene implicaciones importantes porque entonces el intérprete cargará ese directorio como camino para acceder a los archivos mediante la localización relativa.

En Windows lo más normal es ejecutar un script desde la interfaz gráfica. El comportamiento del intérprete será cargar el directorio en el que se encuentre el script para que el mismo pueda acceder al entorno a partir de su posición relativa.

Nota

SAGE es un bicho raro en lo que respecta a archivos porque se trata de una aplicación web.

Para cargar una instancia de un objeto file basta con utilizar la función open(), para la que no tenemos que importar ningún módulo

>>> fh = open('tipos.rst','r')

El segundo argumento se refiere a los permisos con los que abrimos el archivo, en este caso con permisos de sólo lectura. A partir de ahí ya disponemos de todos los elementos necesarios para leer el archivo.

>>> print fh.readline()Tipos, o cómo programar en Python

>>> print fh.readline()=================================

En este caso la variable fh dipone de los métodos necesarios tanto para leer línea a línea o devolver cada una de las líneas del archivo como una lista o para leerlo byte a byte en un estilo más parecido a C.

Page 28: Introduccion a Python Para Ingenieros

La misma clase que nos permite escribir archivos también nos permite leerlos, siempre que se trate de texto.

Al final debemos acordarnos de cerrar el archivo para no ir perdiendo memoria por ahí.

>>> fh.close()

Formato binario

Uno puede leer y escribir números como si fuera texto, uno es libre de hacerlo, pero es una estupidez de un tamaño tan estremecedor que debería estar tipificado como delito con pena de cárcel. Si uno quiere guardar números, como una matriz o una ristra de datos, lo mejor es guardarlo en formato binario del mismo modo que el sistema lo representa en memoria. Es sin duda la manera más eficiente de hacerlo.

Esto es independiente de cómo se abra, se lea, o se cierre el archivo. El archivo no es distinto, lo único que cambia es su contenido.

Esto abre una casuística sobre cómo representar los números y los metadatos asociados como las dimensiones o la precisión. Por suerte numpy y los módulos pickle y cpickle harán el trabajo sucio por nosotros.

La clase listYa hemos hablado sobre las listas pero es importante que les demos un segundo vistazo.

Lo más importante que debemos saber de una lista es que no es un array. No es una buena idea hacer operaciones aritméticas sobre los elementos de una lista porque son una secuencia de elementos que no necesariamente tienen el mismo tipo. Python lo sabe y se va a negar porque no puede multiplicar una letra por un número en coma flotante.

Sin embargo las listas son quizás el tipo más utilizado en Python porque son una manera muy eficiente de operar sobre listas de cosas.

>>> l = str('this is a list of words').split()>>> print l['this', 'is', 'a', 'list', 'of', 'words']>>> l.extend('that I extend now'.split())>>> print l['this', 'is', 'a', 'list', 'of', 'words', 'that', 'I', 'extend', 'now']

Indexación

Ya hemos visto cómo funcionan los subíndices

>>> l.index('list')3>>> l[3]'list'

Lo que aún no sabemos hacer es seleccionar una secuencia dentro de la lista a partir de los índices. Ahí debemos pararnos un instante porque si se entiende bien a la primera no se albergarán dudas al respecto en un futuro.

Cuando en vez de refernrnos a un elemento nos referimos a una secuencia dentro de la lista, un slice, no nos referimos a los índices sino a los intervalos que hay entre los elementos. Esto significa que el primer elemento, el de índice 0, corresponde al slice 0-1. El tercer elemento, de índice 2, corresponde al slice 2-3.

Page 29: Introduccion a Python Para Ingenieros

>>> l[0:1]['this']>>> l[2:3]['a']>>> l[9:10]['now']

Aunque podemos acceder al último elemento tal como se muestra en el ejemplo, disponemos de un atajo para no tener que saber cómo de larga es la lista que mejora si recordamos que podemos utilizar índices negativos:

>>> l[:1]['this']>>> l[-1:]['now']

La cosa se puede complicar. Supongamos que queremos los elementos entre el tercero y el antepenúltimo

>>> l[2:-2]['a', 'list', 'of', 'words', 'that', 'I']

Y que encima los queremos en el órden inverso

>>> l[-2:2:-1]['extend', 'I', 'that', 'words', 'of', 'list']

Supongo que con esto es suficiente

Cualquier objeto que disponga de la función __getitem__() puede indexarse y si dispone también de la función __getslice__() podremos también obtener secuencias. Hay un montón de clases que disponen de estos dos métodos como str, tuple o, la que más nos interesa a nosotros, array

Comprehension expressions

Algunas veces queremos generar una lista a través de una secuencia y una condición más o menos compleja que no se reduce a alguno de los métodos de una lista. Por ejemplo obtener de la lista de palabras anterior y por orden las palabras que contienen la letra o. Podemos hacerlo mediante un bucle.

>>> for w in l:... if 'o' in w:... s.append(w)...>>> print s['of', 'words', 'now']

O podemos generar directamente la lista con un comprehension

>>> s = [w for w in l if 'o' in w]>>> print s['of', 'words', 'now']

La sintaxis de estas sentencias generadoras es prácticamente la frase en inglés: la palabra para cada palabra en la lista si la letra está en la palabra. Sencillo.

También nos permite empezar a utilizar la sintaxis de Python como auténticos profesionales.

>>> print str(' ').join([w.capitalize() for w in l])'This Is A List Of Words That I Extend Now'

Page 31: Introduccion a Python Para Ingenieros

Una breve introducción a la programación orientada a objetos¶

Page 32: Introduccion a Python Para Ingenieros

Un pequeño viaje por la biblioteca estándar.

El módulo os

El módulo sys

El módulo shutil

El módulo datetime¶

Page 33: Introduccion a Python Para Ingenieros

Numpy y la clase arrayUn array es un conjunto de valores con el mismo tipo, esto es, todos sus elementos son enteros, reales en doble precisión, complejos en simple precisión...

Python ya dispone de un tipo array que sirve para almacenar elementos de igual tipo pero no proporciona toda la artillería matemática necesaria como para hacer operaciones de manera rápida y eficiente. De este modo, siempre que nos refiramos a la clase array siempre nos referiremos a la que viene con el módulo numpy

Si consultamos la documentación de numpy nos cuenta lo siguiente:

Numpy proporciona:

1. Un objeto tipo array para datos homogéneos de tipo arbitrario 2. Operaciones matemáticas rápidas para dichos arrays 3. Rutinas para álgebra lineal, transformadas de Fourier y generación de números

pseudoaleatorios.

Es, en sentido estricto, una parte mínima que permite convertir Python en un lenguaje apto para Cálculo Numérico.

Instalar numpyDesde el punto de vista de la instalación, numpy no es distinto de cualquier otro módulo de Python. El tipo de instalación cambia bastante en función del sistema operativo que estemos utilizando. En Linux es recomendable instalar la versión disponible para la distribución correspondiente. Hay también instaladores para Windows y Mac.

Quizás lo más adecuado es instalar alguna versión empaquetada de Python que incluya todas las librerías relacionadas con cálculo científico como puede ser EPD (Enthought Python Distribution) o PythonX,Y.

Importar numpyNo es demasiado recomendable hacer un from numpy import *. Esto importaría una cantidad bastante considerable de funciones y clases y, si estamos trabajando con algún otro módulo relacionado con cálculo numérico, es muy probable que estemos provocando un conflicto de nombres. En prácticamente toda la literatura sobre numpy se importa como:

>>> import numpy as np

O lo que es lo mismo, importar todo numpy dentro del namespace np. Esto no es más que una abreviatura de simplemente hacer

>>> import numpy

Los recortes de código de estos apuntes bien usarán el prefijo numpy o el np, sin un control especialmente estricto. Simplemente hay que tener en cuenta que los dos nombres son equivalentes.

Page 34: Introduccion a Python Para Ingenieros

La clase arrayLa clase array será, a partir de este momento, la herramienta básica para los recortes, los ejercicios y los ejemplos. No podemos hacer numérico en Python sin array.

Para crear un array con determinados valores lo más normal es generarlo a partir de una lista.

>>> a = np.array([[1,2],[3,4]],dtype='double')>>> print a[[ 1. 2.] [ 3. 4.]]

Acabamos de crear un array de 2 filas y 2 columnas de reales de doble precisión. Aunque la lista de listas que hemos utilizado para crear el array contuviera sólo números enteros (no hemos puesto ningún punto después de cada uno de los números) el argumento dtype sirve para especificar la precisión.

En el siguiente ejemplo crearemos un array vacío de números en coma flotante de simple precisión con la función empty()

>>> b = np.empty([5,5],dtype=np.float32)>>> print b[[ -1.32853384e-05 1.45904930e-33 1.55866143e-33 1.55876722e-33 1.32851727e-33] [ 1.55863498e-33 1.72551991e-33 1.55871433e-33 1.72653965e-33 1.55875400e-33] [ 1.72652496e-33 1.55870110e-33 1.33484143e-33 1.72555224e-33 1.72649557e-33] [ 1.33462396e-33 1.72646619e-33 1.47818708e-33 1.50808284e-33 1.27814146e-33] [ 1.31751905e-33 -1.13159913e-05 1.47921638e-33 1.80231790e-33 1.31749995e-33]]

Acabamos de crear un array vacío, esto significa que lo que hemos obtenido son 25 números ordenados en 5 filas y 5 columnas de lo que hubiera en ese preciso instante en la memoria, aunque el resultado de esto no tenga sentido.

La función empty() es la manera más eficiente de alocatear memoria, aunque ya sabemos que alocatear no es necesario en Python. Si queremos generar un array y además inicializarlo con algo que tenga sentido podemos utilizar la función :func:zeros

>>> b = np.zeros([5,5],dtype=np.float32)>>> print b[[ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.]]

Indexación

n-dimensionalidad

MatlabEs innegable que una de las inspiraciones de numpy es Matlab. La idea es intentar aprovechar todo lo bueno y corregir lo que no tiene sentido o está mal diseñado.

Page 35: Introduccion a Python Para Ingenieros

Uno de los grandes méritos de Matlab es el disponer de tal cantidad de funciones para generar y manipular arrays que ha llegado a cambiar el lenguaje en el que se comunican muchos científicos e ingenieros. Uno incluso puede oír por ahí un linspace o un meshgrid. Como el afán de Python es el de no reinventar la rueda podemos encontrar estas mismas funciones con ese mismo nombre en numpy

La clase matrixYa hemos visto que la clase array es más parecida a los arrays que encontramos en C o en Fortran que a las miatrices de Matlab; todas las operaciones aritméticas se ejecutan elemento a elemento.

Esto puede ser un inconveniente si nuestro cerebro ha enfermado por culpa de Matlab y cada vez que vemos una multiplicación entre dos arrays pensamos en la multiplicación matricial. La solución es utilizar la clase matrix en vez de la clase array, teniendo en cuenta que sólo es útil en el caso bidimensional. Esta clase cambia los métodos correspondientes a la multiplicación y la potencia para que sea su equivalente matricial, y no el escalar.

Podemos generar una matriz a partir de un array utilizando el método asmatrix()

Page 36: Introduccion a Python Para Ingenieros

Scipy. Haciendo Python mejor que Matlab

Page 37: Introduccion a Python Para Ingenieros

Matplotlib. Gráficos en 2D.De todos los módulos disponibles orientados a cálculo científico, matplotlib es quizás el más descaradamente inspirado en Matlab.

Se trata de un paquete para la representación de gráficos planos (en 2D) tanto o más potente que el propio Matlab que utiliza todas las convenciones de llamadas propias de Matlab intentando que se comporten de la manera más parecida posible. Por ejemplo, disponemos de las funciones plot(), xlabel(), title(), axis()...

Prácticamente todo lo que se puede hacer con Matlab se puede hacer con Matplotlib. De hecho en la página web del proyecto hay una galería de ejemplos bastante impresionante.

Importante

Obviamente todo lo que se puede hacer con Matlab programando. Matplotlib no dispone una interfaz gráfica de tipo point-and-click para aspirantes a mono-lanzado-al-espacio que le da al ratón aleatoriamente a todo lo que ven. Nada de darle a un botón y que Matlab genere un script que reproduce la figura. Estos apuntes sirven para aprender a hacer cosas, no para que las cosas nos hagan a nosotros. Si habéis llegado a este capítulo esperando encontrar cosas así buscad en otro sitio y bebed un poco de cicuta a mi salud.

Page 38: Introduccion a Python Para Ingenieros

Cálculo simbólico con Sympy.

Page 39: Introduccion a Python Para Ingenieros

Soluciones a los ejercicios propuestos

Ejercicio 1if __name__ == '__main__': f0 = 1 f1 = 1 n = 1 print f0,f1, while n < 20: f0, f1 = f1, f1+f0 print f1, n += 1

Que tiene como resultado

>>>1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946

Ejercicio 2El ejercicio puede resolverse con un simple vistazo a la ayuda del módulo random

>>> import random>>> random.seed()>>> random.sample(xrange(50),5)[46, 43, 38, 25, 8]

Page 40: Introduccion a Python Para Ingenieros

Módulo financeclass finance.yahoostock(symbol, **kwargs)[source]

Bases: object

Wrapper class for Yahoo Finance chart data. The only mandatory argument is the signature of the stock as a string.

Keyword arguments sdate – Initial time for the data as datetime.date or datetime.datetime edate – Final time for the data as datetime.date or datetime.datetime weekly – If it is True gives weekly data monthly – If it is True gives monthly data

Example. IBM_stock = yahoostock(‘IBM’,sdate=datetime.date(2011,1,30),weekly=True)

adj_close[source]

I Don’t really know what this is

clse[source]

Value at the end of the session

date[source]

Date returned as a float

high[source]

Highest value during the session

low[source]

Lowest value during the session

open[source]

Value at the beginning of the session

read()[source]

Reads the data from Yahoo finance

volume[source]

Volume traded during the sessino

Page 41: Introduccion a Python Para Ingenieros

Módulo turbulenceclass turbulence.Vorticity2D(Lx, Ly, Re, CFL)[source]

Bases: object

Solves Navier-Stokes equations in 2D using the vorticity-current function formulation for incompressible flows.

Original code from Adrian Lozano, March 22nd 2011 Modified version in Matlab by Guillem Borrell, May 23rd 2011 Ported to Python by Guillem Borrell, December 30th 2011

FW()[source]

Solve the right hand side, both linear and nonlinear terms

corr2d()[source]

Returns the array of non shifted 2d correlations.

omega[source]

Transforms vorticity from Fourier to physical space to make pretty plots.

set_initial(omega)[source]

Set initial vorticity field once the instance has been created. Make sure that the array is (self.nx,self.ny) shaped or you will be on serious trouble.

step()[source]

Integrates a single Runge Kutta time step.

It uses a fourth order low-storage RK scheme and the timestep is evaluated at the first substep.

velocities()[source]

Returns the velocity components of the result obtained from the vorticity field.

class turbulence.Vorticity2DSerial(Lx, Ly, Re, CFL)[source]

Bases: turbulence.Vorticity2D

Class Vorticity 2D extended with fortran. Serial version of FFTW used. Requires the rhs_tur2d module properly compiled.

FW()[source] cleanup()[source]

Page 42: Introduccion a Python Para Ingenieros

step()[source]

Integrates a single Runge Kutta time step. Calls the Fortran optimized routine.

It uses a fourth order low-storage RK scheme and the timestep is evaluated at the first substep.

turbulence.test_kh(fign, Lx, Ly, nsteps)[source]

Test a Kelvin-Helmholtz instability

turbulence.test_tur2d(fign, Lx, Ly, nsteps)[source]

Test a vortex soup

Page 44: Introduccion a Python Para Ingenieros

ÍndiceA | C | D | F | H | L | O | R | S | T | V | Y

Aadj_close (finance.yahoostock atributo)

Ccleanup() (turbulence.Vorticity2DSerial método) clse (finance.yahoostock atributo)

corr2d() (turbulence.Vorticity2D método)

Ddate (finance.yahoostock atributo)

Ffinance (módulo) FW() (turbulence.Vorticity2D método)

(turbulence.Vorticity2DSerial método)

Hhigh (finance.yahoostock atributo)

Llow (finance.yahoostock atributo)

Oomega (turbulence.Vorticity2D atributo) open (finance.yahoostock atributo)

Rread() (finance.yahoostock método)

Sset_initial() (turbulence.Vorticity2D método) step() (turbulence.Vorticity2D método)