4 Apuntes-Python
4 Apuntes-Python
4 Apuntes-Python
de python
3
Ernesto Aranda
Título: Apuntes de Python 3
Octubre 2018
2 El lenguaje Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3 Aspectos avanzados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4 NumPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.2 Funciones para crear y modificar arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
4.3 Slicing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.4 Operaciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.5 Broadcasting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.6 Otras operaciones de interés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.7 Indexación sofisticada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
4.8 Un ejemplo del uso del slicing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
4.9 Lectura de ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.10 Búsqueda de información . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.11 Aceleración de código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.12 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5 SciPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7 SymPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
1 1
INSTALACIÓN DE PYTHON
Python viene instalado por defecto en los sistemas Linux y OSX, y se pue-
de instalar de forma sencilla en los sistemas Windows desde la página oficial
www.python.org. Sin embargo, diversos módulos de interés, y entre ellos, los dedi-
cados a la programación científica que veremos en los capítulos 4, 5, 6 y 7, requieren
de instalaciones separadas. Existen diversas posibilidades para realizar la instalación
de otros módulos, pero nosotros vamos a optar por una solución simple y eficiente,
que consiste en la instalación de la distribución de Python anaconda.
anaconda Python es una distribución que contiene el núcleo básico de Python
y un conjunto de módulos entre los que se encuentran todos los que vamos a
emplear en este texto. Además incluye por defecto la consola IPython y el entorno
IPython Notebook que veremos en las siguientes secciones, entre otras herramientas
de interés. La descarga de esta distribución se puede realizar desde la página de la
empresa que la desarrolla https://www.anaconda.com/download
Allí encontraremos descargas para los sistemas Linux, Windows y Mac en ver-
siones para 32 y 64 bits, y en la versión 2.7 o 3.6 de Python (en el momento de
escribir estas notas). Como hemos comentado antes, aquí usaremos la versión 3 de
Python, por lo que habría que descargar el instalador para la versión 3.6. En la
misma página de descarga tenemos instrucciones directas para su instalación, que
son bastante simples.
Durante la instalación en los sistemas Windows se pregunta si queremos que el
intérprete Python que instala anaconda sea el intérprete por defecto en el sistema,
ya que anaconda convive bien con otras versiones de Python en el mismo sistema, y
si queremos añadir anaconda a la variable PATH. Responderemos afirmativamente a
ambas cuestiones. De igual modo, en los sistemas Linux se nos pedirá que ajustemos
la variable PATH del sistema para que esté accesible el entorno anaconda.
Una vez instalado, podemos ejecutar el programa anaconda-navigator que
aparece en la lista de programas (en Windows o Mac) o desde la consola en Linux,
que nos permitirá ejecutar alguno de los programas que comentaremos en la siguiente
sección.
1 2
MANEJO BÁSICO DE PYTHON
En esta sección veremos algunos aspectos generales relacionados con el uso del
intérprete y la creación de scripts, para, en la siguiente sección, describir el entorno
IPython Notebook (ahora denominado Jupyter Notebook) que recomendamos en-
carecidamente para trabajar con Python.
Inicialmente en Python podemos trabajar de dos formas distintas: a través
de la consola o mediante la ejecución de scripts o guiones de órdenes. El primer
método es bastante útil cuando queremos realizar operaciones inmediatas y podemos
compararlo con el uso de una calculadora avanzada. El uso de scripts de órdenes
corresponde a la escritura de código Python que es posteriormente ejecutado a través
del intérprete.
Para iniciar una consola Python bastará escribir la orden python en una termi-
1.2 Manejo básico de Python 9
que nos informa de la versión que tenemos instalada y nos señala el prompt >>>
del sistema, el cual indica la situación del terminal a la espera de órdenes. Podemos
salir con la orden exit() o pulsando las teclas ctrl + D ( ctrl + Z en Windows)
Una vez dentro del intérprete podemos ejecutar órdenes del sistema, por ejemplo
>>> print("Hola Mundo ")
Hola Mundo
>>>
Obviamente la función print imprime la cadena de texto (o string) que aparece como
argumento, y que va encerrada entre comillas para indicar precisamente que se trata
de un string. Una vez ejecutada la orden y mostrado el resultado, el intérprete vuelve
a mostrar el prompt.
y dando permisos de ejecución al archivo con la orden chmod a+x hola.py desde una
consola. En tal caso podemos ejecutarlo escribiendo ./hola.py en una consola.5
1 En lo que sigue, usaremos un sistema Linux, pero es sencillo adaptarse a otros sistemas.
Por ejemplo en Windows, podemos abrir una terminal con el programa Anaconda Prompt
instalado con la distribución anaconda.
2 Para diferenciar la escritura de órdenes en el intérprete de los comandos que introduci-
el fichero con Notepad, por ejemplo, a la hora de guardarlo deberíamos seleccionar All Files
en el tipo de archivo. En caso contrario se guardará con extensión .txt.
4 Esto es lo que se conoce como el shebang, y es el método estándar para poder ejecutar un
programa interpretado como si fuera un binario. Windows no tiene soporte para el shebang.
5 En muchos sistemas el intérprete Python por defecto es el de la versión 2, por lo que
Cuando queremos escribir una orden de longitud mayor a una línea debemos
usar el carácter de escape \ como continuación de línea, tanto en el intérprete como
en los scripts:
>>> print("esto es una orden \
... de más de una línea")
esto es una orden de más de una línea
>>> 15 - 23 + 38 \
... -20 + 10
20
Python usa el salto de línea como fin de sentencia a menos que haya paréntesis,
corchetes, llaves o triples comillas abiertas, en cuyo caso no es necesario el carácter
de escape. Por ejemplo,
>>> (24 + 25
... - 34)
15
1 2 2 La consola IPython
En lugar del intérprete Python habitual existe una consola interactiva deno-
minada IPython con una serie de características muy interesantes que facilitan el
trabajo con el intérprete. Entre ellas podemos destacar la presencia del autocomple-
tado, característica que se activa al pulsar la tecla de tabulación y que nos permite
que al teclear las primeras letras de una orden aparezcan todas las órdenes disponi-
bles que comienzan de esa forma. También existe un operador ? que puesto al final
de una orden nos muestra una breve ayuda acerca de dicha orden, así como acceso
al historial de entradas recientes con la tecla
De forma idéntica a la apertura de una consola Python, escribiendo ipython en
un terminal obtenemos:6
Python 3.6.1 | Anaconda 4.4.0 (64 - bit)| (default , May 11 2017 ,
13:09:58)
Type " copyright ", " credits " or " license " for more information .
In [1]:
Obsérvese que ahora el prompt cambia, y en lugar de >>> aparece In [1]:. Cada
vez que realizamos una entrada el número va aumentando:
In [1]: 23*2
Out [1]: 46
In [2]:
Si como ocurre en este caso, nuestra entrada produce una salida Out[1]: 46,
podemos usar la numeración asignada para reutilizar el dato mediante la variable
_1,
In [2]: _1 + 15
Out [2]: 61
que hace referencia al valor almacenado en la salida [1]. En cualquier consola
Python, el último valor obtenido siempre puede usarse mediante _,
In [3]: _ * 2 # _ hace referencia al último valor
Out [3]: 122
In [4]: _2 + _
Out [4]: 183
Además, esta consola pone a nuestra disposición comandos del entorno (cd,
ls, etc.) que nos permiten movernos por el árbol de directorios desde dentro de la
consola, y comandos especiales, conocidos como funciones mágicas (magic functions)
que proveen de funcionalidades especiales a la consola. Estos comandos comienzan
por el carácter % aunque si no interfieren con otros nombres dentro del entorno se
puede prescindir de este carácter e invocar sólo el nombre del comando. Entre los
más útiles está el comando run con el que podemos ejecutar desde la consola un
script de órdenes. Por ejemplo, para ejecutar el creado anteriormente:
In [5]: run hola.py
Hola Mundo
El lector puede probar a escribir % y pulsar el tabulador para ver un listado de
las funciones mágicas disponibles.
1 3
IPYTHON NOTEBOOK
el entorno Jupyter, que soporta otros lenguajes, además de Python. Es una forma
muy interesante de trabajar con Python pues aúna las buenas características de la
consola IPython, con la posibilidad de ir editando las entradas las veces que sean
necesarias. Además, permiten añadir texto en diferentes formatos (LATEX inclusive)
e incluso imágenes, por lo que se pueden diseñar páginas interactivas con código e
información.
Puesto que este es el entorno que preferimos
para trabajar, describiremos con un poco de deta-
lle su funcionamiento general. Para correr el en-
torno podemos escribir en una terminal la orden
jupyter-notebook,7 lo que nos abrirá una ventana
en un navegador web con un listado de los notebooks
disponibles y la posibilidad de navegar en un árbol
de directorios, así como de editar ficheros desde el
navegador. Los notebooks son ficheros con extensión
.ipynb que pueden ser importados o exportados con
facilidad desde el propio entorno web.
Si no disponemos de un notebook previo, pode-
Nuevo Notebook
mos crear uno nuevo pulsando sobre el desplegable
New (arriba a la derecha), eligiendo el tipo deseado, en nuestro caso Python 3 (véase
la figura adjunta). Esto abre automáticamente una nueva ventana del navegador a
la vez que crea un nuevo fichero Untitled con extensión .ipynb en la carpeta donde
nos encontremos. La nueva ventana del navegador nos muestra el notebook creado,
en la que podemos cambiar el título fácilmente sin más que clicar sobre el mismo.
El concepto básico del entorno Jupyter son las celdas,
que son cuadros donde insertar texto que puede admitir
diferentes formatos de entrada que pueden seleccionarse
a través del menú desplegable del centro de la barra de
herramientas (véase la figura adjunta).
Básicamente nos interesan las celdas tipo Code, que
contendrán código en lenguaje Python, y que aparecerán Tipos de celdas
numeradas como en la consola IPython, y las de tipo
Markdown, en las que podemos escribir texto marcado por este tipo de lenguaje,8 o
incluso texto en formato LATEX, que nos permite añadir información contextual al
código que estemos escribiendo.
Por ejemplo, si en una celda estilo Markdown escribimos lo siguiente:
# Cabecera
Cabecera
que es el resultado de interpretar el símbolo # antes de una palabra, que supone
darle formato de título de primer nivel. Si la entrada hubiera sido:
7O lanzarlo a través de Anaconda Navigator.
8 Markdown es un lenguaje de marcado ligero que permite formatear de forma fácil y
legible un texto.
1.3 IPython Notebook 13
### Cabecera
Cabecera
es decir, el símbolo ### se refiere a una cabecera de tercer nivel. En el menú
del notebook Help Markdown se puede acceder a la sintaxis básica del lenguaje
Markdown.
Cada notebook dispone de una barra de herramientas típica para guardar, cortar,
pegar, etc., y botones para ejecutar el contenido de una celda o para,en caso de
necesidad, interrumpir la ejecución. Otras funciones están accesibles a través del
menú. Aquí sólo citaremos la opción File Make a Copy... , que realiza una copia del
notebook y File Download as que proporciona una exportación del notebook a un
archivo de diverso formato: desde el propio formato .ipynb, a un fichero .py con el
contenido de todas las celdas (las de tipo Markdown aparecen como comentarios), o
también ficheros html o pdf, entre otros.
Para cerrar un notebook usaremos la opción del menú File Close and Halt . Para
cerrar completamente el entorno debemos volver a la terminal desde la que ejecu-
tamos la orden jupyter-notebook y pulsar ctrl + c ; a continuación se nos pedirá
confirmación para detener el servicio, que habrá que hacer pulsando y . Si por error
hemos cerrado la ventana Home del navegador podemos recuperarla en la dirección
http://localhost:8888
Si ya disponemos de un notebook y queremos seguir trabajando sobre él, podemos
abrirlo desde la ventana Home del navegador moviéndonos en el árbol de directorios
hasta encontrarlo. Obsérvese que por restricciones de seguridad, el servicio no da
acceso a directorios por encima del de partida, que coincide con el directorio desde el
que se ha ejecutado la orden jupyter-notebook. Para poder cargar un notebook que
no esté accesible de este modo, debemos usar el botón de Upload que nos permitirá
localizar en nuestro ordenador el fichero adecuado.
Para finalizar con esta breve introducción a Jupyter, queremos hacer referencia
al estupendo conjunto de atajos de teclado disponibles que permite realizar ciertas
tareas de forma rápida, como crear celdas por encima o por debajo de la celda activa,
juntar o dividir el contenido de celdas, definir el tipo de celda, etc. La información
está accesible desde el menú Help Keyboard shortcuts .
2 El lenguaje Python
En este capítulo comenzaremos a ver los aspectos básicos del lenguaje: variables,
módulos, bucles, condicionales y funciones. Usaremos multitud de ejemplos para
ilustrar la sintaxis del lenguaje y lo haremos desde un entorno Jupyter Notebook,
por lo que el código irá apareciendo en celdas de entrada y sus correspondientes
salidas.
2 1
ASPECTOS BÁSICOS DEL LENGUAJE
2 1 1 Variables numéricas
Veamos algunos ejemplos:
a = 2 # define un entero
b = 5. # define un número real
c = 3+1j # define un número complejo
d = complex (3 ,2) # define un número complejo
Obsérvese la necesidad de poner un punto para definir el valor como real y no como
entero, el uso de j en lugar de i en los números complejos junto con la necesidad
1 Específicamente, el primer carácter de un identificador ha de ser cualquier carácter
considerado como letra en Unicode. También es posible usar el guión bajo (o underscore) _
aunque éste suele estar reservado para identificadores con un significado especial. El resto
de caracteres pueden ser letras, dígitos o underscore de Unicode.
2 Tampoco es recomendable usar ninguno de los nombres que se obtienen al ejecutar la
15
16 Capítulo 2 El lenguaje Python
< c l a s s ' i n t ' > < c l a s s ' f l o a t ' > < c l a s s ' complex ' >
2 1 2 Operadores aritméticos
Los operadores aritméticos habituales en Python son: + (suma), - (resta), *
(multiplicación), / (división), ** (potenciación, que también se puede realizar con
la función pow), // (división entera), que da la parte entera de la división entre dos
números, y el operador % (módulo), que proporciona el resto de la división entre dos
números.
Asimismo es importante destacar el carácter fuertemente tipado del lenguaje
Python, que puede observarse en los siguientes ejemplos:
a = 5; b = 3
print (a//b)
esto es, la división entera entre 5 y 3 es 1, como número entero.4 Sin embargo,
c = 5.
d = 3.
print (c//d)
3 Por defecto, la coma introduce un espacio entre los argumentos de la función, que
1.0
da como resultado 1, como número real. Esto se debe a que el resultado se expresa en
el mismo tipo que los datos con los que se opera. Así pues, el lector podrá entender
el porqué del siguiente resultado:
print (c % d)
print (int(a) % int(b))
2.0
2
print (5/3)
1.6666666666666667
a = 3.
b = 2+3j
c = a+b # suma de real y complejo
print (c)
print (type(c))
(5+3 j )
< c l a s s ' complex ' >
a = a + b
a += b
2 1 3 Objetos
a = 3+2j
print (a.real)
print (a.imag)
3.0
2.0
Los atributos son características de los objetos a las que se accede mediante el
operador . de la forma objeto.atributo.
Cada tipo de objeto suele tener disponible ciertos métodos. Un método es una
función que actúa sobre un objeto con una sintaxis similar a la de un atributo,
es decir, de la forma objeto.método(argumentos). Por ejemplo, la operación de
conjugación es un método del objeto complejo:
a. conjugate ()
(3 −2 j )
Los paréntesis indican que se trata de una función y son necesarios. En caso
contrario, si escribimos
a. conjugate
el intérprete nos indica que se trata de una función, pero no proporciona lo esperado.
Nótese en los dos últimos ejemplos que hemos prescindido de la función print
para obtener los resultados. Esto es debido a que el entorno Jupyter Notebook
funciona de forma similar a una consola, devolviendo el resultado de una expresión
sin necesidad de imprimirla explícitamente. Sin embargo, si tenemos más de una
sentencia en la misma celda, sólo aparecerá el resultado de la última evaluación.
Este comportamiento por defecto puede ser modificado si escribimos en una celda
de un notebook lo siguiente:
2.1 Aspectos básicos del lenguaje 19
2 1 4 Listas
Las listas son colecciones de datos de cualquier tipo (inclusive listas) que están
indexadas, comenzando desde 0:
list
Como podemos ver, hemos definido una lista encerrando sus elementos (de tipos
diversos) entre corchetes y separándolos por comas. Podemos acceder a cada uno de
los elementos de la lista escribiendo el nombre de la lista y el índice del elemento
entre corchetes, teniendo en cuenta que el primer elemento tiene índice 0 y por tanto
el segundo corresponde al índice 1.
print (a[1])
print (a[4])
2.0
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
IndexError Traceback ( most
recent c a l l l a s t )
< ipython − input −2−7 efc04aa6ae5 > i n <module > ( )
1 print ( a [ 1 ] )
−−−−> 2 p r i n t ( a [ 4 ] )
Sin embargo, si intentamos acceder al elemento a[4] obtenemos un error, pues dicho
elemento no existe.
La salida de error en Python es amplia, y entre otras cosas nos marca el lugar
donde éste se ha producido, el tipo de error (IndexError, en este caso) y en la última
línea nos da una breve explicación. En lo que sigue, para simplificar las salidas de
errores sólo mostraremos la última línea.
Si algún elemento de la lista es otra lista, podemos acceder a los elementos de
esta última usando el corchete dos veces, como en el siguiente ejemplo:
print (a[3])
print (a [3][1])
20 Capítulo 2 El lenguaje Python
[3 ,0]
0
len(a)
Las listas son estructuras de datos muy potentes que conviene aprender a
manejar con soltura. Podemos consultar los métodos a los que tenemos acceso
en una lista usando la función de autocompletado. Los siguientes ejemplos son
autoexplicativos y muestran el funcionamiento de alguno de estos métodos:
[ 2 5 , 33 , 1 , 15 , 33 , 0 ]
[ 2 5 , 33 , 1 , −1 , 15 , 33 , 0 ]
[ 0 , 33 , 15 , −1 , 1 , 33 , 2 5 ]
25
print (a)
[ 0 , 33 , 15 , −1 , 1 , 3 3 ]
−1
[ 0 , 33 , 15 , 1 , 3 3 ]
2.1 Aspectos básicos del lenguaje 21
[ 0 , 33 , 15 , 1 , 33 , 10 , 20 , 3 0 ]
[ 0 , 33 , 15 , 1 , 33 , 10 , 20 , 30 , [ 1 0 , 20 , 3 0 ] ]
Por otra parte, es frecuente que Python utilice los operadores aritméticos con
diferentes tipos de datos y distintos resultados. Por ejemplo, los operadores suma y
multiplicación pueden aplicarse a listas, con el siguiente resultado:
a = [1 ,2 ,3]
b = [10 ,20 ,30]
print (a*3)
print (a+b)
[1 , 2 , 3 , 1 , 2 , 3 , 1 , 2 , 3]
[ 1 , 2 , 3 , 10 , 20 , 3 0 ]
a. extend (b)
print (a)
[ 1 , 2 , 3 , 10 , 20 , 3 0 ]
Nótese que el uso del método hubiera sido equivalente a escribir a+=b usando el
operador de asignación aumentado.
En general, el uso de métodos proporciona mejor rendimiento que el uso de
otras acciones, pero hemos de ser conscientes de que el objeto sobre el que se aplica
puede quedar modificado al usar un método. Un error bastante frecuente consiste
en asignar la salida de un método a una nueva variable. Por ejemplo,
b = a. reverse ()
print (b)
None
[ 1 , 2 , 3 , 10 , 20 , 3 0 ]
[ 3 0 , 20 , 10 , 3 , 2 , 1 ]
Slicing
Una de las formas más interesantes de acceder a los elementos de una lista
es mediante el operador de corte o slicing, que permite obtener una parte de los
elementos de una lista:
a = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print (a [2:5]) # accedemos a los elementos 2,3,4
[7 , 6 , 5]
Como vemos, el slicing [n:m] accede a los elementos de la lista desde n hasta
m (el último sin incluir). Admite un parámetro adicional, y cierta flexibilidad en la
notación:
print (a [1:7:2]) # desde 1 hasta 6, de 2 en 2
[8 , 6 , 4]
[9 , 8 , 7]
[3 , 2 , 1 , 0]
print (a [:20] )
[9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0]
[]
En las listas, y por supuesto también con el slicing, se pueden usar índices
negativos que equivalen a contar desde el final:
[4 , 3]
[6 , 5 , 4 , 3]
a = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
a [1:3] = [] # borra elementos 1 y 2
print (a)
[9 , 6 , 5 , 4 , 3 , 2 , 1 , 0]
[ 9 , 6 , −1 , −2 , −3 , −4 , 2 , 1 , 0 ]
[ 9 , 0 , 1 , 2 , 6 , −1 , −2 , −3 , −4 , 2 , 1 , 0 ]
[ 9 , 20 , 30 , 1 , 2 , 6 , −1 , −2 , −3 , −4 , 2 , 1 , 0 ]
[ − 3 , −4 , 2 , 1 , 9 , 0 , 1 , 2 , 6 , −1 , −2 , −3 , −4 , 2 , 1 , 0 ]
[]
2 1 5 Cadenas de caracteres
Las cadenas no son más que texto encerrado entre comillas:
Hola
mundo
< class ' str ' >
en las que se puede comprobar que da igual definirlas con comillas simples o dobles,
siempre que empiecen y terminen por el mismo carácter, lo que es útil si queremos
cadenas que incluyan estos caracteres:
Si queremos construir cadenas con más de una línea usamos la triple comilla
""":
Esto es un cadena
muy l a r g a que t i e n e
muchas l í n e a s
Y si queremos crear una cadena muy larga sin necesidad de usar el carácter de
continuación, podemos hacerlo del siguiente modo:
\ nhola
hola
H
' '
pero las cadenas son inmutables, esto es, no es posible alterar sus elementos (veremos
este asunto más adelante en la sección 2.5):
26 Capítulo 2 El lenguaje Python
Al igual que con las listas, la función len proporciona la longitud de la cadena
de caracteres, y los operadores + y * (multiplicación por un entero) funcionan como
concatenación y replicación de la cadena, respectivamente.
Hay una gran cantidad de métodos para manejar strings que permiten cambiar
la capitalización, encontrar caracteres dentro de una cadena o separar cadenas en
trozos en función de un carácter dado, entre otros muchos. Emplazamos al lector a
usar la ayuda en línea para aprender el funcionamiento de esos métodos.
2 1 6 Diccionarios
colores = {'r': 'rojo ', 'g': 'verde ', 'b': 'azul '}
type( colores )
dict
rojo
{ 'k ': ' negro ' , 'r ': ' rojo ' , 'b ' : ' azul ' , 'g ' : ' verde ' }
Observar que el orden dentro de los elementos del diccionario es irrelevante pues la
indexación no es numerada, y además no se puede predecir.
2.1 Aspectos básicos del lenguaje 27
El objeto que se usa como índice se denomina clave. Podemos pensar entonces
en un diccionario como un conjunto no ordenado de pares, clave: valor donde
cada clave ha de ser única (para ese diccionario). Podemos acceder a ellas usando
los métodos adecuados:
que devuelven vistas7 de los objetos que contienen las claves y los valores.
A veces es útil disponer de una lista con las claves o los valores, que se puede
obtener con la función list:
Entre los diversos métodos accesibles para un diccionario disponemos del método
pop que permite eliminar una entrada en el diccionario,
negro
{ ' r ' : ' rojo ' , 'g ' : ' verde ' , 'b ' : ' azul ' }
colores . clear ()
print ( colores )
{}
7 Una vista de un objeto es otro objeto que está dinámicamente enlazado con el principal,
de manera que una modificación de éste produce una modificación automática de la vista.
28 Capítulo 2 El lenguaje Python
2 1 7 Conjuntos
Los conjuntos en Python son estructuras de datos desordenadas que no permiten
repetición de elementos, y además éstos han de ser datos inmutables (véase la
sección 2.5). Son especialmente útiles cuando queremos eliminar repeticiones de
datos en otras estructuras. Se pueden definir usando llaves
colors = {'green ', 'blue ', 'red ', 'green ', 'yellow '}
print ( colors )
{ ' yellow ' , ' green ' , ' red ' , ' blue ' }
a = set('abracadabra ')
print (a)
{ 'r ' , 'b ' , 'd ' , 'c ' , 'a ' }
d = {}
a = set ()
2 1 8 Tuplas
Otro de los tipos de datos principales en Python son las tuplas. Las tuplas son
un tipo de dato similar a las listas (es decir, una colección indexada de datos) pero
que no pueden alterarse una vez definidas (son inmutables):
a = (1 ,2. ,3+1j,"hola")
type(a)
2.1 Aspectos básicos del lenguaje 29
tuple
print (a[2])
(3+1 j )
tuple
1
2
hola
Y este procedimiento puede ser llevado a cabo de forma simultánea, lo que da lugar
a la asignación múltiple de variables:
que es particularmente útil, por ejemplo, para intercambiar valores sin necesidad de
usar una variable auxiliar,
30 Capítulo 2 El lenguaje Python
a,b = 0,1
b,a = a,b # intercambiamos a y b
print (a,b)
1 0
i, k, x = 1, 2, [0 ,1 ,2 ,3]
i, x[i] = i+k, i
print (x)
[0 , 1 , 2 , 1]
a, b = 1,2,3
a, *b = 1,2,3
print (a,b)
1 [2 , 3]
o también
a, *b, c, d = 1,2,3,4,5,6
print (a,b,c,d)
1 [2 , 3 , 4] 5 6
2 1 9 Booleanos y comparaciones
En Python disponemos de las variables booleanas True y False y la función bool
que convierte un valor a booleano, siempre y cuando tenga sentido tal conversión. En
Python, al igual que en C, cualquier número distinto de 0 es verdadero, mientras que
8 Esta característica no está presente en Python 2.
2.1 Aspectos básicos del lenguaje 31
0, 0.0 y 0j son falsos. Además, la variable None y las listas, cadenas, diccionarios,
conjuntos o tuplas vacías son falsas; el resto son verdaderas. ]
a = []
b = 1,
c = 'a'
print (bool(a))
print (bool(b))
print (bool(c))
False
True
True
Por otro lado, hay que tener en cuenta que la clase bool es una subclase de los
números enteros, lo que explica el siguiente comportamiento:
a = True
a + a
a = 5
b = 'y'
print (a and b)
print (b and a)
print (a or b)
y
5
5
2 < 3 <= 5
True
False
[ − 4 , −3 , −1 , 1 , 1 , 2 , 3 , 5 , 6 ]
x = list('AbraCadaBra ')
sorted (x)
[ 'A' , 'B ' , 'C ' , 'a ' , 'a ' , 'a ' , 'a ' , 'b ' , 'd ' , 'r ' , 'r ']
Nótese que la función crea una nueva lista mientras que el método la modifica.
Tanto la función como el método admiten un parémetro opcional key que debe ser
una función, y que es llamada para cada uno de los elementos de la lista, realizándose
la ordenación sobre la transformación de dicha lista. Por ejemplo, podemos usar la
función abs (valor absoluto):
[ 1 , −1 , 1 , 2 , −3 , 3 , −4 , 5 , 6 ]
x = list('AbraCadaBra ')
sorted (x,key=str.lower ,reverse=True)
[ 'r ' , 'r ' , 'd ' , 'C ' , 'b ' , 'B ' , 'A ' , 'a ' , 'a ' , 'a ' , 'a ' ]
9 Esto ocurre no sólo con las listas, sino con cualquier objeto iterable. Básicamente, un
Operador de pertenencia
Finalmente, en Python disponemos del operador de pertenencia in, (o no per-
tenencia, not in) que busca un objeto dentro de cualquier otro objeto iterable. Por
ejemplo,
a = [0, 1, 2, 3, 4, 5]
print (7 in a)
print (3 in a)
print (6 not in a)
False
True
True
s = 'cadena '
print ('ca ' in s)
print ('cd ' in s)
True
False
Nótese que en los diccionarios, la búsqueda se realiza en las claves; para buscar
en los valores debemos usar el método values:
True
False
True
2 2
MÓDULOS
sin (3.)
34 Capítulo 2 El lenguaje Python
import math
Ahora tenemos a nuestra disposición todas las funciones del módulo matemático.
Puesto que todo en Python es un objeto (incluidos los módulos), el lector enten-
derá perfectamente que el acceso a las funciones del módulo se haga de la forma
math.función:
math.sin (3.)
0.1411200080598672
Para conocer todas las funciones a las que tenemos acceso dentro de cualquier
objeto disponemos de la orden dir:
dir(math)
[ ' _ _ d o c _ _ ' , ' __name__ ' , ' _ _ p a c k a g e _ _ ' , ' acos ' , ' acosh ' ,
' asin ' , ' asinh ' , ' atan ' , ' atan2 ' , ' atanh ' , ' c e i l ' ,
' copysign ' , ' cos ' , ' cosh ' , ' degrees ' , ' e ' , ' e r f ' ,
' e r f c ' , ' exp ' , ' expm1 ' , ' fabs ' , ' f a c t o r i a l ' , ' f l o o r ' ,
' fmod ' , ' frexp ' , ' fsum ' , 'gamma' , ' hypot ' , ' i s i n f ' ,
' isnan ' , ' ldexp ' , ' lgamma ' , ' log ' , ' log10 ' , ' log1p ' ,
' modf ' , ' pi ' , ' pow ' , ' r a d i a n s ' , ' sin ' , ' sinh ' ,
' s q r t ' , ' tan ' , ' tanh ' , ' trunc ' ]
Usada sin argumentos, la orden dir() devuelve un listado de las variables actual-
mente definidas. Con un objeto como argumento nos proporciona una lista con todos
los atributos y métodos asociados a dicho objeto.
sqrt (2)
1.4142135623730951
2.2 Módulos 35
Lógicamente, esta forma de cargar los módulos tiene ventajas evidentes en cuanto
a la escritura de órdenes, pero tiene también sus inconvenientes. Por ejemplo, es
posible que haya más de un módulo que use la misma función, como es el caso de
la raíz cuadrada, que aparece tanto en el módulo math como en el módulo cmath
(para funciones matemáticas con complejos). De manera que podemos encontrarnos
situaciones como la siguiente:
import math
import cmath
math.sqrt (-1)
V a l u e E r r o r : math domain e r r o r
1j
Como vemos, hemos cargado los módulos math y cmath y calculado la raíz
cuadrada de −1 con la función sqrt que posee cada módulo. El resultado es bien
distinto: la función raíz cuadrada del módulo math no permite el uso de números
negativos, mientras que la función sqrt del módulo cmath sí. Es posible escribir la
misma importación del siguiente modo
¿qué ocurrirá al hacer sqrt(-1)? Como el lector puede imaginar, la función sqrt
del módulo cmath es sobrescrita por la del módulo math, por lo que sólo la última
es accesible.
Existe una tercera opción para acceder a las funciones de los módulos que no
precisa importarlo al completo. Así,
nos deja a nuestra disposición la función raíz cuadrada del módulo cmath y las
funciones trigonométricas seno y coseno del módulo math. Es importante señalar
que con este método de importación no tenemos acceso a ninguna otra función de
los módulos que no hubiera sido previamente importada. Esta última opción es de
uso más frecuente en los scripts, debido a que con ella cargamos exclusivamente las
funciones que vamos a necesitar y de esa forma mantenemos el programa con el
mínimo necesario de recursos.
Durante una sesión interactiva es más frecuente cargar el módulo al completo,
aunque es aconsejable hacerlo sin el uso de *. De hecho, hay una posibilidad adicional
36 Capítulo 2 El lenguaje Python
que nos evita tener que escribir el nombre del módulo al completo, seguido del punto
para usar una función. Podemos realizar una importación abreviada del módulo
como sigue:
import math as m
m.cos(m.pi)
−1.0
Este tipo de importación suele ser más habitual cuando cargamos submódulos,
esto es, módulos que existen dentro de otros módulos, de manera que la escritura
completa se vuelve tediosa. Por ejemplo,
2 2 1 La biblioteca estándar
Una de las frases más escuchadas cuando nos iniciamos en el mundo Python es
que Python trae las pilas incluidas. Con esto se trata de reflejar el hecho de que
Python trae consigo una extensa biblioteca de aplicaciones que nos facilita realizar
un gran cantidad de tareas. Aunque en estas notas sólo veremos con detenimiento
algunos de los módulos relacionados con la computación científica, conviene conocer
algunos otros módulos de la biblioteca estándar como los que se muestran en la
tabla 2.2.
Para obtener un listado de los módulos disponibles podemos usar la función help
help ()
help >
2 3
CONTROL DE FLUJO
2 3 1 Bucles
Una característica esencial de Python es que la sintaxis del lenguaje impone
obligatoriamente que escribamos con cierta claridad. Así, los bloques de código
2.3 Control de flujo 37
Módulo Descripción
math Funciones matemáticas
cmath Funciones matemáticas con complejos
fractions Números racionales
statistics Estadística
os Funcionalidades del sistema operativo
shutil Administración de archivos y directorios
sys Funcionalidades del intérprete
re Coincidencia en patrones de cadenas
datetime Funcionalidades de fechas y tiempos
pdb Depuración
random Números aleatorios
ftplib Conexiones FTP
MySQLdb Manejo de bases de datos MySQL
sqlite3 Manejo de bases de datos SQLite
xml Manejo de archivos XML
smtplib Envío de e-mails
zlib Compresión de archivos
csv Manejo de archivos CSV
json Manejo de ficheros JSON
xmlrpc Llamadas a procedimientos remotos
timeit Medición de rendimiento
collections Más tipos de contenedores
optparse Manejo de opciones en la línea de comandos
38 Capítulo 2 El lenguaje Python
0
1
2
0 1 1 2 2 3
Obsérvese también en este ejemplo el uso del argumento opcional end en la llamada
a la función print que establece el carácter final de impresión, que por defecto es
un salto de línea, y aquí hemos cambiado a un simple espacio, lo que conlleva que
las impresiones realizadas se hagan en la misma línea.
[5 , 6 , 7 , 8 , 9]
[1 , 4 , 7]
[ − 1 0 , −40 , −70]
La diferencia entre la lista generada por range y el objeto range es que la primera
reside en la memoria de forma completa, mientras que la segunda no, lo que la hace
computacionalmente más eficiente.
11 En Python 2, range creaba un objeto tipo lista, pero en Python 3, esta orden es un
h o l a
m u n d o
colores = {'r': 'rojo ', 'g': 'verde ', 'b': 'azul '}
for i in colores :
print(i, colores [i])
g verde
r rojo
b azul
g verde
r rojo
b azul
2 3 2 Condicionales
La escritura de sentencias condicionales es similar a la de los bucles for, usando
los dos puntos y el sangrado de línea para determinar el bloque:
if 5 %3 == 0:
print("5 es divisible entre 3")
elif 5 %2 == 0:
print("5 es divisible por 2")
else:
print("5 no divisible ni por 2 ni por 3")
5 no es d i v i s i b l e n i por 2 n i por 3
Es posible poner todos los elif que sean necesarios (o incluso no ponerlos), y el
bloque else no es obligatorio.
2 3 3 Bucles condicionados
Un bucle condicionado es un bloque de código que va a ser repetido mientras
que cierta condición sea cierta. La estructura la determina la sentencia while.
Obsérvese el siguiente ejemplo:
0 1 1 2 3 5 8 13
Es interesante analizar un poco este breve código que genera unos cuantos términos
de la sucesión de Fibonacci. En especial, hemos de prestar atención a cómo usamos
tuplas para las asignaciones múltiples que realizamos en la primera y última línea;
en la primera hacemos a=0 y b=1 y en la última se realiza la asignación a=b y b=a+b,
en la que debemos notar que, antes de realizar la asignación, se evalúan los lados
derechos (de izquierda a derecha). El bloque va a seguir ejecutándose mientras que
el valor de b sea menor que 20.
Interrupción y continuación
También disponemos de las sentencias break y continue para terminar o con-
tinuar, respectivamente, los bucles for o while. Asimismo, la sentencia else tiene
sentido en un bucle y se ejecuta cuando éste ha finalizado completamente, en el caso
de for, o cuando es falso, y no se ha interrumpido, en el caso de while.
Veamos los siguientes ejemplos:
2 es primo
3 es primo
4 es igual a 2 * 2
5 es primo
6 es igual a 2 * 3
7 es primo
8 es igual a 2 * 4
9 es igual a 3 * 3
2.3 Control de flujo 41
k = 1; mynumber = 7
while k < 5:
if k == mynumber :
break
print(k,end=' ')
k += 1
else:
print("No")
1 2 3 4 No
vemos que la parte del else es ejecutada en este caso, pues el bucle ha finalizado
sin interrupción (mynumber = 7 hace que no se llegue a ejecutar la sentencia break);
pero si ponemos
k = 1; mynumber = 3
while k < 5:
if k == mynumber :
break
print(k,end=' ')
k += 1
else:
print("No")
1 2
entonces el bucle es interrumpido puesto que ahora sí alcanzamos el valor que hemos
establecido para mynumber, y por tanto la parte de else no se ejecuta.
Por último, Python dispone también de la orden pass que no tiene ninguna
acción, pero que en ocasiones es útil para estructurar código que aún no ha sido
completado, por ejemplo:
2 4
FUNCIONES DEFINIDAS POR EL USUARIO
Las funciones son trozos de código que realizan una determinada tarea. Vienen
definidas por la orden def y a continuación el nombre que las define seguido de dos
puntos. Siguiendo la sintaxis propia de Python, el código de la función está sangrado.
La principal característica de las funciones es que permiten pasarles argumentos de
manera que la tarea que realizan cambia en función de dichos argumentos.
Como puede verse, esta función imprime los términos de la sucesión de Fibonacci
menores que el valor k introducido.
Si quisiéramos almacenar dichos términos en una lista, podemos usar la orden
return que hace que la función pueda devolver algún valor, si es necesario:
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 8 9 ]
a = fibolist (250)
print (a)
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144 , 233]
a = fibo (50)
0 1 1 2 3 5 8 13 21 34
print (a)
None
Cuando queremos devolver más de un valor en una función nos bastará empa-
quetarlos como una tupla.
Aunque las funciones pueden ser definidas dentro del intérprete para su uso, es
más habitual almacenarlas en un fichero, bien para poder ser ejecutadas desde el
mismo, o bien para ser importadas como si se tratara de un módulo. Para guardar
el contenido de una celda del entorno Jupyter a un archivo, bastará usar la magic
cell,12 % %writefile seguida del nombre del archivo:
% %writefile fibo.py
def fibolist (k):
a,b = 0,1
sucesion = [a]
while b < k:
sucesion . append (b)
a, b = b, a+b
return sucesion
x = 100
print ( fibolist (x))
$ python fibo.py
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
run fibo.py
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 8 9 ]
12 A diferencia de las funciones mágicas, que se denotan con un único signo de porcentaje,
las celdas mágicas son instrucciones del entorno Jupyter que afectan a todo el contenido de
la celda en la que se encuentran y llevan un doble signo de porcentaje.
44 Capítulo 2 El lenguaje Python
import fibo
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 8 9 ]
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 3 4 ]
print (fibo.x)
100
Sin embargo,
print (x)
Nótese cómo al cargar el módulo, éste se ejecuta y además nos proporciona todas
las funciones y variables presentes en el módulo (inclusive las importadas por él),
pero anteponiendo siempre el nombre del módulo. Obsérvese que la variable x no
está definida, mientras que sí lo está fibo.x.
Si realizamos la importación con * (reiniciar previamente el núcleo):
x = 5
from fibo import *
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 8 9 ]
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144]
100
tendremos todas las funciones presentes en el módulo (salvo las que comiencen por
_) sin necesidad de anteponer el nombre, pero como podemos observar en el ejemplo,
podemos alterar las variables propias que tengamos definidas, razón por la cual no
recomendamos este tipo de importación.
Finalmente, si realizamos la importación selectiva: (reiniciar primero el núcleo)
2.4 Funciones definidas por el usuario 45
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 8 9 ]
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 2 1 ]
NameError : name ' x ' i s not d e f i n e d
Lo que sucede es que cuando ejecutamos el código con python fibo.py desde
la consola (o con run fibo.py desde el entorno Jupyter), la variable especial 13
__name__ toma el valor '__main__', por lo que el fragmento final se ejecuta, lo
que no ocurrirá si lo importamos.
Es importante resaltar que por razones de eficiencia, los módulos se importan
una sola vez por sesión en el intérprete, por lo que si son modificados es necesario
reiniciar la sesión o bien volver a cargar el módulo con la orden reload(módulo) del
módulo importlib.
un doble guión bajo (doble underscore, conocido en el mundo Python como dunder).
14 Una variable de entorno es un valor que controla un determinado comportamiento en
2 5
COPIA Y MUTABILIDAD DE OBJETOS
Hemos mencionado anteriormente que las tuplas y las cadenas de caracteres son
objetos inmutables, mientras que las listas son mutables. Debemos añadir también
que los diferentes tipos de números son inmutables y los diccionarios y conjuntos son
mutables. Ahora bien, ¿qué significa exactamente que los números sean inmutables?
¿Quiere decir que no los podemos modificar?
En realidad estas cuestiones están relacionadas con el modo en el que Python usa
las variables. A diferencia de otros lenguajes, en los que una variable esencialmente
referencia una posición en memoria, cuyo contenido podemos modificar, en Python,
una variable en realidad no es más que una referencia al objeto, no contiene su valor,
sino una referencia a él. Podríamos decir que el operador de asignación en Python
no realiza una copia de valores, sino un etiquetado de los mismos.
Lo podemos ver con un sencillo ejemplo; la orden id nos muestra el identificador
de un objeto, el cual podemos pensar como su dirección en memoria. Se trata de un
identificador único para cada objeto que está presente. Si escribimos:
x = 5 # x apunta al objeto 5
id(x)
160714880
lo primero que hace Python es crear el objeto 5, al que le asigna la variable x. Ahora
entenderemos por qué ocurre lo siguiente:
y = 5 # y apunta al objeto 5
id(y)
160714880
No hemos creado un nuevo objeto, sino una nueva referencia para el mismo objeto,
por lo que tienen el mismo identificador. Lo podemos comprobar con la orden is:
x is y
True
x = x + 2 # x apunta al objeto 7
id(x)
160714856
id (7)
160714856
y ahora:
x is y
False
Con las listas pasa algo similar, salvo que ahora está permitido modificar el
objeto (porque son mutables), por lo que las referencias hechas al objeto continuarán
apuntado a él:
a = [1 ,2]
id(a) # identificador del objeto
139937026980680
[3 , 2]
139937026980680
[0 , 1 , 2 , 3]
48 Capítulo 2 El lenguaje Python
[0 , 1 , 2]
[0 , 1 , 2 , 3]
x[:5] = []
print (x)
print (y)
[5 , 6 , 7 , 8 , 9]
[0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9]
Sin embargo, la copia mediante slicing es lo que se conoce como una copia
superficial (shallow copy):
x = [1, [1 ,2]]
y = x[:]
x [1][1] = 0
print (y)
[1 , [1 , 0]]
vemos que también se ha modificado la copia. ¿Por qué ocurre esto? La copia
superficial crea una copia de las referencias a cada uno de los elementos de la lista,
por lo que si uno de dichos elementos es un objeto mutable, volvemos a tener el
mismo problema: aunque lo modifiquemos, sus referencias apunta al mismo objeto.
Para hacer una copia completamente independiente hemos de realizar lo que se
denomina como una copia profunda (deep copy), para la que necesitamos el uso del
módulo copy:
import copy
x = [1, [1 ,2]]
y = copy. deepcopy (x)
x [1][1] = 0
print (y)
[1 , [1 , 2]]
Nótese también que este módulo posee una función copy que realiza una copia
superficial de los objetos, es decir, es equivalente a la copia por slicing para las
listas.16
Como hemos mencionado antes, los diccionarios y conjuntos son también objetos
mutables, lo que significa que tienen el mismo comportamiento que las listas:
{ 'k ': ' negro ' , 'r ': ' rojo ' , 'g ' : ' verde ' }
c = a.copy ()
a.pop('r')
print (a)
print (c)
{ 'g ': ' verde ' , ' k ' : ' negro ' }
{ 'r ': ' r o j o ' , ' g ' : ' verde ' , ' k ' : ' negro ' }
Para copias profundas habría que usar la función deepcopy del módulo copy.
2 5 1 La orden del
Aunque la orden del sugiere el borrado de un objeto, el funcionamiento en
Python está relacionado con la forma en la que se usan las referencias a objetos.
16 Desde la versión 3.3 de Python las listas poseen también el método copy que realiza
Si usamos del sobre una variable, o mejor dicho, sobre una referencia a un objeto,
lo que se hace es eliminar dicha referencia al objeto. Si el objeto no posee ninguna
otra referencia, entonces Python lo pone accesible para el recolector de basura que
finalmente se encargará de eliminarlo. Por ejemplo,
x = 8
print (x)
del x
print (x)
En objetos mutables, como las listas, del puede aplicarse a elementos o slices
eliminándolos de dicha lista:
a = [1, 2, 3, 4, 5]
del a[:2]
print (a)
[3 , 4 , 5]
La orden del es útil para eliminar referencias que quedan activas después de
realizar ciertas operaciones. Por ejemplo, si hacemos un bucle
0 1 2 2
vemos que después del bucle, la variable x sigue existiendo. Para evitar esto podría-
mos escribir del x y eliminar dicha referencia.
En ocasiones, si la variable del bucle no se usa dentro de éste,17
2 1 4 2 2
17 La función randint del módulo random genera números enteros aleatorios en el rango
dado.
18 Recuérdese que en el entorno IPython o Jupyter Notebook, el significado de esta variable
1 4 2 5 5
import os.path
archivo = " nombre .pdf"
_ , ext = os.path. splitext ( archivo )
print (ext)
2 6
INSTALACIÓN DE MÓDULOS EXTERNOS
En la sección 2.4.2 hemos visto cómo importar un módulo creado por nosotros
de manera directa, pero suele ser habitual que los programas más elaborados
posean diversos módulos distribuidos en diferentes directorios, siguiendo una cierta
jerarquía, configurando lo que se denomina un paquete. Si bien es posible modificar
la variable sys.path para que el intérprete pueda encontrar el directorio oportuno,
esto puede ser perjudicial en caso de que se repitan módulos con el mismo nombre.20
Para esta situación, Python dispone de un mecanismo sencillo consistente en
añadir, en cada directorio que queramos considerar como un paquete, un archivo
__init__.py, que en los casos más simples puede estar vacío, y en otros puede
ejecutar código encargado de inicializar el paquete. Por ejemplo, supongamos que
tenemos una carpeta de nombre solvers con el contenido estructurado en la figu-
ra 2.1.
Supongamos además que en el archivo system.py hay una función denominada
sistema, y en los archivos gauss.py, jacoby.py y seidel.py existen sendas fun-
ciones gauss_method, jacoby_method y seidel_method, respectivamente. Obsérvese
que en cada carpeta del módulo existe un archivo __init__.py que hará que cada
subcarpeta sea considerada como un submódulo. Si consideramos inicialmente que
todos los archivos __init__.py están vacíos, y suponiendo que la carpeta solvers
está accesible a Python, entonces para poder acceder a la función sistema tendría-
mos que realizar la importación del siguiente modo:
19 La función splitext del submódulo os.path devuelve una tupla con el nombre y la
solvers
__init__.py
system.py
direct
__init__.py
gauss.py
indirect
__init__.py
jacoby.py
seidel.py
y tener acceso directo a la función sistema. Para poder usar las funciones definidas
en gauss_method habría que escribir
import solvers
import solvers
tendremos bajo el objeto solvers todas las funciones del módulo. Este tipo de
importación usando rutas relativas es especialmente útil si en alguno de los módulos
de nuestro paquete queremos otro módulo del mismo paquete.
En definitiva, cuando tenemos diversos módulos en diferentes carpetas, este tipo
de configuraciones nos permitirá acceder convenientemente a ellos.
esta sección vamos a abordar la cuestión de cómo instalar un módulo creado por
terceros que nos hayamos descargado desde internet.21
En principio, bastaría descargarse el archivo oportuno, descomprimirlo y dar
acceso a Python a la carpeta creada (algo similar a lo comentado con la carpeta
solvers). Sin embargo, la situación más habitual es la de encontrarnos con un
módulo que ha sido empaquetado de una forma específica con setuptools, un
módulo de Python para construir y distribuir paquetes. Reconoceremos fácilmente
este tipo de paquetes, pues en la carpeta principal aparecerá un archivo setup.py. El
procedimiento de instalación clásico en estos casos consiste en situarse en la carpeta
en cuestión y escribir en la terminal
Con esa orden se ejecutarán todas las tareas necesarias para instalar el módulo en
algún lugar en el que Python podrá encontrarlo desde cualquier sitio,22 sin necesidad
de modificar el PYTHONPATH o sys.path.
Si no tenemos permiso para poder escribir en determinadas carpetas donde se
instalan los módulos por defecto siempre es posible realizar una instalación local.
La opción más simple consiste en usar la opción --user en la orden anterior:
$ export PYTHONPATH=/ruta/donde/instalar
El sitio oficial desde donde poder descargar todo tipo de paquetes es PyPI24
que es un repositorio que en la actualidad consta de más de 120000 paquetes. Una
vez encontrado el paquete deseado podemos descargarlo e instalarlo tal y como
21 Ni que decir tiene que hemos de ser precavidos a la hora de ejecutar código cuya
Para hacerlo permanente hay que definir esta variable en el archivo oportuno.
24 the Python Package Index — https://pypi.python.org/pypi
2.7 Ejercicios 55
hemos comentado en la sección anterior, pero también es posible usar pip, una
herramienta para gestionar la instalación de paquetes Python desde PyPI. Desde la
página principal de PyPI podemos obtener pip (básicamente se trata de descargar
el archivo get-pip.py y ejecutarlo con el intérprete). Si no se tienen los permisos
adecuados será necesario realizar una instalación local mediante
2 7
EJERCICIOS
a = (1)
b = (1 ,)
c = 1,
a = [1 ,2]
print (a. index (1))
print (a. index (2))
a[a. index (1)], a[a. index (2)] = 2,1
print (a)
0
1
[1 , 2]
s=0
for i in range (100) :
s += i
print (s)
E2.8 Construir una función que calcule la media aritmética y la media geométrica
de una lista de valores que entra como argumento. La media aritmética es
x1 + x2 + · · · + xn
x̄ =
n
y la geométrica es
√
x̃ = n
x1 · x2 · · · xn
E2.10 Escribe una función mcd que calcule el máximo común divisor de dos
números enteros.
2.7 Ejercicios 57
E2.11 Escribir una función reverse que acepte una cadena de caracteres y
devuelva como resultado la cadena de caracteres invertida en orden, es decir, el
resultado de reverse('hola') debe ser 'aloh'.
E2.12 Usando el ejercicio E2.11, escribir una función is_palindromo que inden-
tifique palíndromos, (esto es, palabras que leídas en ambas direcciones son iguales,
por ejemplo radar). Es decir, is_palindromo('radar') debería dar como resultado
True.
E2.13 Modifica la función fibolist de la página 43 para que admita dos paráme-
tros de entrada, k y p y que devuelva el primer elemento de la sucesión de fibonacci
menor que k y múltiplo de p.
E2.14 Dado un número natural cualquiera, por ejemplo, 1235, se trata de crear
una función que imprima el número tal y como sigue:
digitos =
[cero ,uno ,dos ,tres ,cuatro ,cinco ,seis ,siete ,ocho ,nueve]
3 1
ALGO MÁS SOBRE FUNCIONES
3 1 1 Documentando funciones
Se puede documentar una función en Python añadiendo una cadena de docu-
mentación justo detrás de la definición de función:
Absolutamente nada
"""
pass
3 1 2 Argumentos de entrada
Es posible definir funciones con un número variable de argumentos. Una primera
opción consiste en definir la función con valores por defecto:
pero
fibonacci (b=4,a=2)
TypeError : f i b o n a c c i ( ) missing 1 r e q u i r e d p o s i t i o n a l
argument : ' k '
el * significa que todos los argumentos que aparecen después han de ser explicitados
en la llamada. Es decir, podemos seguir usando
fibonacci (100)
pero ya no
pues a partir del primer argumento hemos de explicitar los demás; es decir, llamadas
válidas podrían ser:
3.5
5.75
otro
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
(2 , 3 , 4)
****************************************
{}
funarg ("hola",a=2,b=4)
62 Capítulo 3 Aspectos avanzados
hola
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
()
****************************************
{ ' a ' : 2 , ' b ' : 4}
hola
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
( ' uno ' , ' dos ' , ' t r e s ' )
****************************************
{ 'a ': 2, 'c ': ' fin ' , ' b ' : 3}
Por esta razón es muy habitual ver la cabecera de muchas funciones en Python
escritas del siguiente modo:
5.5
o en la función fibonacci,
d = {'a':5, 'b':3}
fibonacci (50 ,**d)
[ 5 , 3 , 8 , 11 , 19 , 30 , 4 9 ]
c = (2 ,3)
fibonacci (50 ,*c)
[ 2 , 3 , 5 , 8 , 13 , 21 , 3 4 ]
3 1 3 Funciones lambda
Las funciones lambda son funciones anónimas de una sola línea que pueden ser
usadas en cualquier lugar en el que se requiera una función. Son útiles para reducir
el código, admiten varios parámetros de entrada o salida y no pueden usar la orden
return:
3.1 Algo más sobre funciones 63
(5 , 8)
[ [ 0 , 1] , [1 , −5] , [1 , 2] , [2 , 0] , [3 , −1]]
[ [ 1 , −5] , [3 , −1] , [2 , 0] , [0 , 1] , [1 , 2 ] ]
Las variables globales son aquéllas definidas en el cuerpo principal del programa,
mientras que las variables locales son aquellas variables internas a una función que
sólo existen mientras la función se está ejecutando, y que desaparecen después. Por
ejemplo, si definimos la siguiente función
y ejecutamos
y luego
print (area)
area = 10
def area_cuadrado (lado):
area = lado*lado
return area
9
10
Como vemos, la variable area ahora sí existe en el espacio de nombres global, pero
su valor no se ve alterado por la función, pues ésta ve su variable area de manera
local.
¿Y si tratamos de acceder al valor de area antes de su evaluación?
area = 10
def area_cuadrado (lado):
print(area)
area = lado*lado
return area
area = 10
def area_cuadrado (lado):
print(area)
return lado*lado
print ( area_cuadrado (3))
10
9
area = 10
def area_cuadrado (lado):
global area
print(area)
area = lado*lado
return area
10
9
print (area)
ha cambiado su valor, debido a que ya no hay una variable local area en la función,
sino que se trata de una variable global.
def fun(a,b):
a. append (0)
b = a+b
return a,b
a = [1]
b = [0]
print (fun(a,b))
([1 , 0] , [1 , 0 , 0])
La función fun usa el método append para agregar el elemento 0 a la lista de entrada
a, quedando por tanto la lista modificada por el método. Por su parte, la asignación
que hacemos en b=a+b crea una nueva variable local b dentro de la función, que no
altera a la variable b de fuera de ese espacio. De este modo,
print (a,b)
[1 , 0] [ 0 ]
66 Capítulo 3 Aspectos avanzados
es decir, a ha sido modificada, pero b no, aun siendo ambas variables de tipo lista.
Sin embargo, en el primer caso, la lista es modificada mediante un método, mientras
que en el segundo, se crea una variable local que es la que se modifica.
a = 2
def pot(x,y=a):
return x**y
pot (2) , pot (2 ,3)
(4 , 8)
a = 4
pot (2)
a = 2
def pot(x):
return x**a
print (pot (2))
a = 3
print (pot (2))
4
8
insertar ([0])
3.1 Algo más sobre funciones 67
[0 , 1]
insertar ()
[1]
insertar ()
[1 , 1]
insertar ()
[1 , 1 , 1]
3 1 5 Recursividad
Python permite definir funciones recurrentes, es decir, que se llamen a sí mismas,
lo cual es un método inteligente para resolver cierto tipo de problemas. El ejemplo
típico de recursividad aparece cuando definimos el factorial de un número natural
k, que se define por:
k! = k · (k − 1) · (k − 2) · · · 2 · 1
esto es, el producto de todos los números naturales menores o iguales que el número
dado. Si observamos que
k! = k · (k − 1)! (3.1)
es sencillo escribir una función recursiva para realizar el cálculo:
mifactorial (1000)
3 2
ENTRADA Y SALIDA DE DATOS
Es habitual que nuestros programas necesiten datos externos que deben ser
pasados al intérprete de una forma u otra. Típicamente, para pasar un dato a un
programa interactivamente mediante el teclado (la entrada estándar) podemos usar
la función input
Introduce algo
hola 2
' hola 2 '
import sys
print (sys.argv)
Como vemos, sys.argv almacena en una lista todos los datos de la llamada, de
manera que es fácil manejarlos teniendo presente que son considerados como cadenas
de caracteres, por lo que es probable que tengamos que realizar una conversión de
tipo para tratarlos correctamente.
Por ejemplo, si tenemos el archivo fibo.py con el siguiente contenido
3.2 Entrada y salida de datos 69
import sys
$ python fibo.py 30
[0, 1, 1, 2, 3, 5, 8, 13, 21]
$ python fibo.py
Traceback (most recent call last):
File "fibo.py", line 14, in <module >
x = int(sys.argv [1])
IndexError : list index out of range
run fibo.py 30
[ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 2 1 ]
3 2 1 Salida formateada
x = 3; y = 4
print ("El valor de 'x' es " + str(x) + " y el de 'y' es "
+ str(y))
donde es preciso convertir el entero a string con la función str. Algunos métodos
para los strings, como por ejemplo ljust o rjust permiten mejorar la salida: 1
0 0
1 1
4 8
9 27
16 64
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
En este caso, el contenido del interior de las llaves hace referencia a los elementos
del método format que le sigue, de manera que los números que van antes de los
dos puntos corresponden a los índices del objeto que sigue a format y lo que va
después de los dos puntos define el modo en el que se imprimirán dichos objetos. A
continuación mostramos algunos ejemplos:
ab ra ca da bra
También podemos usar nombres de variables en lugar de índices para pasar los
argumentos de format:
1 ljust o rjust llamado con un parámetro (que denota la longitud total de la cadena a
P a r t e r e a l : 1 . 0 ; P a r t e compleja : 2 . 0
P a r t e r e a l : 3 . 0 ; P a r t e compleja : 1 . 0
P a r t e r e a l : − 2 . 0 ; P a r t e compleja : −5.0
alineación a izquierda
a l i n e a c i ó n a derecha
alineación centrada
Por último, señalar que si en la cadena de caracteres que vamos a formatear con
el método format aparecen llaves que deseamos imprimir, éstas han de escribirse
por duplicado:
Esto va e n t r e { l l a v e s }
3 2 2 Lectura de archivos
La orden open crea un objeto tipo archivo que nos permitirá la lectura y/o
escritura en el mismo. La sintaxis de la orden es
El Ingenioso Hidalgo
Don Quijote de la Mancha
E l Ingenioso Hidalgo
Don Q u i j o t e de l a Mancha
El método read del objeto f creado lee el archivo al que referencia dicho objeto. Si
se le proporciona un argumento numérico entonces lee un número determinado de
caracteres del archivo;2 cuando es llamado sin argumento, lee el archivo al completo.
Cada llamada a read mueve el puntero de lectura del archivo al último lugar leído,
de manera que la llamada siguiente lee a partir de ahí. Por eso:
f.read ()
''
produce una cadena vacía, pues el puntero se halla al final del archivo.
Podemos usar el método seek para situarnos en cualquier posición del archivo.
2 Hay que tener en cuenta que también hay que considerar los caracteres de tabulación,
f.read (4)
leemos los siguientes 4 caracteres, uno de los cuales es el caracter de fin de lí-
nea. Nótese la diferencia entre el método f.read() y la impresión resultante de
print(f.read()): en el primero la cadena que contiene el archivo es leída en for-
mato de representación interna, que difiere de la representación que se muestra con
print, en el que la cadena es impresa.
También es posible leer línea a línea con readline
f.seek (0)
f. readline ()
Don Q u i j o t e de l a Mancha
f. readline ()
''
o con f.readlines() que almacena cada una de las líneas del archivo en una lista:
f.seek (0)
for x in f. readlines ():
print(x,end='')
f.seek (0)
for x in f:
print(x,end='')
También existe una forma de manejar los archivos que aisla los errores que
puedan ocurrir y que cierra automáticamente el archivo una vez que estemos fuera
del entorno. Se trata de la orden with que funciona del siguiente modo:
3 2 3 Escritura
La escritura en un archivo se lleva a cabo con el método write. Para ello es
necesario que el archivo se abra en modo escritura (o lectura/escritura):
Una vez fuera del entorno debemos volver a abrir para leerlo:
primera
segunda
tercera
import sys
g = sys. stdout
g. write('Hola mundo \n')
Hola mundo
lo que puede ser útil para programar las salidas por pantalla durante la elaboración
de código con write, de modo que cuando queramos direccionar la salida a un
archivo sólo necesitaremos cambiar la salida.
De forma similar funciona el parámetro file de la función print, el cual
redirecciona la salida hacia el objeto que determinemos. Por ejemplo,
Mucho más agresivo sería redireccionar toda la salida por pantalla a un archivo,
reescribiendo la variable stdout. Si hacemos,
3 3
LISTAS POR COMPRENSIÓN, ITERADORES Y GENERADORES
a = range (5)
[x**2 for x in a] # cuadrados de los elementos de a
[0 , 1 , 4 , 9 , 16]
a = range (10)
[x**3 for x in a if x %2 == 0] # cubo de los múltiplos de 2
[ 0 , 8 , 64 , 216 , 512]
una forma de escribir código que usa las características propias y peculiares del lenguaje
Python que auna sencillez, brevedad, elegancia y potencia.
76 Capítulo 3 Aspectos avanzados
[2 , 4 , 6]
[1 , 3 , 5]
[ 2 , 6 , 10 , 4 , 12 , 20 , 6 , 18 , 3 0 ]
[ 2 , 12 , 3 0 ]
[ 2 , 12 , 3 0 ]
{ ' i ' : 3 , ' e ' : 5 , ' o ' : 6 , ' a ' : 9 , 'u ' : 2}
True
{ 6 5 , 39 , 13 , 78 , 52 , 26 , 91}
3 3 1 Iteradores
En una lista, podemos obtener simultáneamente el índice de iteración y el
elemento de la lista con la orden enumerate. El lector podrá imaginar el resultado
del siguiente ejemplo:
1 2 3
b = iter(a)
for x in b:
print(x,end=' ')
1 2 3
pues al fin y al cabo, cuando hacemos un bucle sobre un iterable, en realidad lo que
se hace es definir el iterador asociado a dicho iterable y recorrerlo. En un iterador
tenemos definido el método __next__ al que podemos acceder también con la función
next, que nos proporciona el siguiente elemento de la secuencia. En los iterables no
tenemos esta posibilidad:
a = 'hola '
next(a)
78 Capítulo 3 Aspectos avanzados
b = iter(a)
next(b)
'h '
3 3 2 map y filter
Una alternativa a la creación rápida de listas es el uso de la función map que
permite aplicar una función a una lista. Por ejemplo, podemos incluir los dígitos de
un número en una lista del siguiente modo. En primer lugar convertimos el número
a cadena de caracteres, y a continuación lo convertimos en lista:
a = 1235151
b = list(str(a))
print (b)
[ '1 ' , '2 ' , '3 ' , '5 ' , '1 ' , '5 ' , '1 ']
de este modo tenemos una lista con los dígitos, pero como cadena de caracteres.
Si ahora queremos convertir las cadenas a números, podemos usar una lista por
comprensión usando la función int:
[int(x) for x in b]
[1 , 2 , 3 , 5 , 1 , 5 , 1]
o bien usar la función map cuyos argumentos serán la función que vamos a aplicar a
cada uno de los elementos de la lista (o iterable), y el iterable:
map(int ,b)
Podemos ver que el resultado no parece ser el esperado, pero en realidad lo que
hemos construido es un iterador. Podemos obtener fácilmente la lista si hacemos:
list(map(int ,b))
[1 , 2 , 3 , 5 , 1 , 5 , 1]
Pero si lo que queremos es iterar sobre la lista, será mucho más eficiente iterar sobre
el objeto map pues, a diferencia de la lista, que es almacenada completamente en
3.3 Listas por comprensión, iteradores y generadores 79
memoria, este objeto sólo almacena en memoria el siguiente objeto a iterar. Por
ejemplo, podemos recuperar el número inicial con el siguiente bucle:
s = 0
for i,x in enumerate (map(int , reversed (b))):
s += 10**i*x
print (s)
1235151
La función map es eficiente combinada con lambda, y puede trabajar sobre más
de una secuencia:
a = [1, 3, 5]
b = [2, 4, 6]
list(map( lambda x,y: x*y,a,b))
[ 2 , 12 , 3 0 ]
Por su parte, filter permite filtrar todos los elementos de una secuencia que
satisfacen cierta condición. Funciona de forma parecida a map, con la excepción de
que la función suministrada ha de devolver un booleano:
15
3 3 3 Generadores
Una de las ventajas de map frente a las listas por comprensión es que no almacena
la lista resultante en memoria, sino sólo la generación de ésta, de forma que su
rendimiento es muy alto. No obstante, se puede conseguir lo mismo con las listas
por comprensión si en lugar de corchetes usamos paréntesis. En tal caso, lo que
obtenemos es un generador, que es un tipo especial de iterador, que se crea de
forma más sencilla, bien mediante el mecanismo de las listas por comprensión con
paréntesis (las denominadas expresiones generadoras), o bien mediante una función
generadora que básicamente es una función que en lugar de return usa yield.
El comportamiento de una función generadora es curioso: la primera vez que
llamamos a la función, cuando encontramos yield se retorna el valor obtenido como
es habitual, pero los valores locales de la función y el puntero de ejecución son
80 Capítulo 3 Aspectos avanzados
Una vez más estamos creando la sucesión de Fibonacci, pero en este caso sin límite,
y los estamos retornando con yield. Si ahora hacemos
g = fibonacci ()
0 1 1 2 3 5 8 13 21 34
Los generadores consumen los valores utilizados y no podemos volver a ellos, por
lo que si nuevamente hacemos
3 4
EXCEPCIONES
for a in [1 ,0]:
b = 1/a
print(b)
1.0
Z e r o D i v i s i o n E r r o r : d i v i s i o n by zero
Para evitarla, hacemos uso de las órdenes try y except del siguiente modo:
for a in [1 ,0]:
try:
b = 1/a
print (b)
except :
print ("Se ha producido un error ")
1.0
Se ha producido un e r r o r
for a in [0,'1']:
try:
b = 1/a
print (b)
except ZeroDivisionError :
print (" División por cero")
except TypeError :
print (" División inválida ")
D i v i s i ó n por cero
División inválida
1.0
No se puede r e a l i z a r l a d i v i s i ó n
No se puede r e a l i z a r l a d i v i s i ó n
Sólo el tipo de excepción que coinciden con alguna de las que aparecen junto a
except, o que es compatible con alguna de éstas,7 son tratadas en ese bloque. El
resto genera un mensaje de error usual.
En general,suele ser recomendable tratar cada tipo de excepción de forma
individual, pero cuando usamos varios bloques except debemos prestar atención
al orden en el que se pone cada excepción. Obsérvese el siguiente ejemplo:
a = [0 ,1 ,2]
a[3] = 0
a = [0 ,1 ,2]
try:
a[3] = 0
except LookupError :
print("Error en la búsqueda ")
except IndexError :
print("Error de indexación ")
E r r o r en l a búsqueda
else y finally
La estructura try–except admite un bloque opcional con else que debe aparecer
después de todas las sentencias except. El bloque else se ejecutará si el bloque
try no produce ninguna excepción. En principio, el código del bloque else se
podría poner junto con el del bloque try, pero podría ocurrir que generase un
error capturado por la excepción que estuviéramos omitiendo. Obsérvese el siguiente
ejemplo:
7 Las excepciones se agrupan por clases siguiendo una jerarquía de dependencias entre
una clase y sus clases derivadas. Decimos que las excepciones son compatibles si pertenecen
a la misma clase o a cualquiera de sus clases derivadas.
3.4 Excepciones 83
for a in [1,'1']:
try:
b = 1/a
print (b)
f = open('archivo .txt ','r')
except :
print ("No se puede realizar la división ")
1.0
No se puede r e a l i z a r l a d i v i s i ó n
No se puede r e a l i z a r l a d i v i s i ó n
for a in [1,'1']:
try:
b = 1/a
print (b)
except :
print ("No se puede realizar la división ")
else:
f = open('archivo .txt ','r')
1.0
File No tFound Err or : [ Errno 2 ] No such f i l e or d i r e c t o r y :
' archivo . txt '
Por otra parte, podemos añadir a un bloque try–except una cláusula final
para ser ejecutada en cualquier circunstancia. Para ello disponemos de la sentencia
finally que marcará un bloque de código que se ejecutará antes de abandonar el
bloque try, con independencia de que se produzca o no una excepción.
El bloque finally se ejecutará siempre, incluso si la excepción no ha sido
capturada o se ha producido en el bloque except o else, o aunque abandonemos el
bloque try mediante alguna otra interrupción (vía break, continue o return).
En el ejemplo siguiente:
a = 1
try:
b = 1/a
print(b)
except :
print("No se puede realizar la división ")
else:
f = open('archivo .txt ','r')
finally :
print('ejecución final ')
1.0
ejecución f i n a l
File NotFound Err or : [ Errno 2 ] No such f i l e or d i r e c t o r y :
' archivo . txt '
vemos que, aunque se produce un error antes de llegar al bloque finally, éste se
ejecuta y luego se muestra la excepción.
Opcionalmente, es posible obtener información específica de cualquier excepción
mediante la siguiente estructura:
1.0
3 4 1 Lanzar excepciones
La orden raise nos permite lanzar una determinada excepción, o la última
excepción capturada (si estamos dentro de un bloque try–except).
3.5 Otras sentencias útiles 85
NameError : Mensaje de e r r o r
raise
RuntimeError : No a c t i v e e x c e p t i o n to r e r a i s e
da lugar a un error de ejecución que nos informa de que no hay capturada ninguna
excepción. Mientras que dentro de un bloque try–except, relanza dicha excepción:
a = 0
try:
b = 1/a
except Exception :
print("Ha ocurrido un error ")
raise
Ha o c u r r i d o un e r r o r
Z e r o D i v i s i o n E r r o r : d i v i s i o n by zero
3 5
OTRAS SENTENCIAS ÚTILES
3 5 1 any y all
Python dispone de las funciones any y all que aplicadas sobre un iterable
devuelven True si alguno (para any) o todos (para all) sus elementos son ciertos.
Si el iterable es vacío, any devuelve False y all devuelve True.
En el ejemplo siguiente, creamos una lista de 5 enteros aleatorios entre 1 y 4 y
vemos si hay alguno par,
import random
a = [ random . randint (1 ,4) for x in range (5)]
print (a)
any ([ not x %2 for x in a])
[3 , 1 , 1 , 1 , 1]
False
all(x %2 for x in a)
86 Capítulo 3 Aspectos avanzados
True
Nótese que en este último ejemplo hemos usado un generador en lugar de una lista.
3 5 2 Expresiones condicionales
def fun(x):
return x**2 if not x % 2 else x / 2
print (fun (10))
print (fun (11))
100
5.5
Entre los diversos métodos para manejar cadenas de caracteres está el método
join que permite unir una colección de cadenas. Es especialmente útil cuando quere-
mos concatenar diversas cadenas usando el mismo string como elemento intermedio.
Por ejemplo:
a = "hola"
''.join( reversed (a))
a = 1235
for y in range (7):
print(' '.join ([ digitos [x][y] for x in
map(int ,str(a))]))
es una lista por compresión con las números impresos con *. Finalmente hay que
recorrer uno a uno cada elemento que compone esta lista (que es lo que lleva a cabo
el bucle for).
3 6
EJERCICIOS
X
100
1
E3.1 Calcula √
i=1
i
E3.2 Calcula la suma de los números pares entre 1 y 100.
E3.3 Calcular la suma de todos los múltiplos de 3 o 5 que son menores que 1000.
Por ejemplo, la suma de todos los múltiplos de 3 o 5 que son menores que 10 es:
3 + 5 + 6 + 9 = 23
E3.4 Utiliza la función randint del módulo random para crear una lista de 100
números aleatorios entre 1 y 10.
E3.5 El Documento Nacional de Identidad en España es un número finalizado
con una letra, la cual se obtiene al calcular el resto de la división de dicho número
entre 23. La letra asignada al número se calcula según la siguiente tabla:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
TRWAGMYFPD X B N J Z S Q V H L C K E
Escribe un programa que lea el número por teclado y muestre la letra asignada. Por
ejemplo, al número 28594978 le debe corresponder la letra K. Tres líneas de código
debería ser suficientes.
E3.6 Usando un bucle while escribe un programa que pregunte por un número
por pantalla hasta que sea introducido un número impar múltiplo de 3 y de 7 que
sea mayor que 100.
E3.7 Crea una lista aleatoria de números enteros (usar ejercicio E3.4) y luego
escribe un programa para que elimine los valores impares de dicha lista.
E3.8 Escribir una función histograma que admita una lista aleatoria de números
enteros entre 1 y 5 (usar ejercicio E3.4) y elabore un diagrama de frecuencias de
aparición de cada uno de los elementos de la lista según el formato que sigue: por
ejemplo, si la lista es
88 Capítulo 3 Aspectos avanzados
[1, 4, 2, 1, 4, 4, 5, 5, 5, 3]
1 **
2 *
3 *
4 ***
5 ***
E3.9 Escribir una función menorymayor que acepte un conjunto indeterminado
de números y obtenga el mayor y el menor de entre todos ellos. Es decir, la función
debería hacer lo siguiente:
menorymayor (2 ,5 ,1 ,3 ,6 ,2)
(1 ,6)
(1 ,10)
E3.10 Escribir una función que acepte como parámetros dos valores a y b, y una
función f tal que f (a)f (b) < 0 y que, mediante el método de bisección, devuelva
un cero de f . La aproximación buscada será controlada por un parámetro ε que por
defecto ha de ser 10−4 .
E3.11 Considera la siguiente función:
1.0
varios (1, 2)
1
2
varios (1, 2, 4, 3)
1
2
****
***
varios (3,4,a=1,b=3)
3
4
−
−
varios (2,5,1,a=2,b=3)
4 1
ARRAYS
import numpy as np
1 Parte de la ventaja computacional que ofrece NumPy estriba en este aspecto, pues las
operaciones entre arrays no tendrán que chequear el tipo de cada uno de sus elementos.
91
92 Capítulo 4 NumPy
[ 1. 3. 6.]
print (type(a))
print (a. dtype )
Nótese que en NumPy, los tipos de datos numéricos son un poco más extensos,
incluyendo reales en doble precisión (float64) y simple precisión (float32), entre
otros. Por defecto, el tipo float de Python, corresponde a float64. Es importante
tener en cuenta el tipo con el que se define un array, pues pueden ocurrir errores no
deseados:
int64
array ( [ 2 , 3 , 5 ])
Como el array es entero, todos los datos son convertidos a ese tipo. No obstante,
podemos cambiar de tipo todo el array,
[ 2. 3. 5.]
a[0] = 3.6
print (a)
[ 3.6 3. 5. ]
Para evitarnos el tener que cambiar de tipo lo más sencillo es definir desde el
principio el array usando elementos del tipo deseado, o indicándolo expresamente
como en el siguiente ejemplo:
4.1 Arrays 93
[ [ 1. 3. 5.]
[ 2. 1. 8.]]
y el acceso a los elementos puede hacerse con el doble corchete, o el doble índice:
5.0 5.0
4 1 1 Atributos básicos
Tenemos diversos atributos y funciones para obtener información relevante del
array:
(2 , 3)
2 in a # el elemento 2 está en a
94 Capítulo 4 NumPy
True
4 in a # el elemento 4 no está en a
False
4 2
FUNCIONES PARA CREAR Y MODIFICAR ARRAYS
array ( [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])
array ( [ 5 , 6, 7, 8, 9 , 10 , 11 , 12 , 13 , 1 4 ] )
array ( [ 2 , 5 , 8 ])
Nótese que todos los arrays creados son enteros. No obstante, esta orden también
admite parámetros reales
útiles para dividir un intervalo en subintervalos de igual longitud. Nótese que el final
no se incluye.
Si queremos dividir un intervalo con un número preciso de subdivisiones, tenemos
la orden linspace:
4 2 1 Modificación de forma
Con el método reshape podemos cambiar la forma de un array:
array ( [ [ 0 , 1 , 2 , 3 , 4] ,
[ 5 , 6 , 7 , 8 , 9] ,
[ 1 0 , 11 , 12 , 13 , 1 4 ] ] )
o también,
array ( [ [ 0 , 3, 6 , 9 , 12] ,
[ 1, 4, 7 , 10 , 1 3 ] ,
[ 2, 5, 8 , 11 , 1 4 ] ] )
La opción por defecto equivale a order='C'. Las iniciales 'C' y 'F' corresponden a
los lenguajes C y FORTRAN, respectivamente, y se refieren al modo en el que estos
lenguajes almacenan los datos multidimensionales.
El método transpose, que se puede abreviar como .T, en arrays bidimensionales
es equivalente a la transposición de matrices:2
[[0 5]
[1 6]
[2 7]
[3 8]
[4 9]]
print (a)
print (a. flatten ())
[ [ 0 1 2 3 4]
[5 6 7 8 9 ] ]
[0 1 2 3 4 5 6 7 8 9]
[0 5 1 6 2 7 3 8 4 9]
[ 0. 0. 0. 0. 0 . ]
[ [ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
[ [ 1. 1. 1. 1.]
[ 1. 1. 1. 1.]]
O bien
[ [ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
[ [ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]]
4.2 Funciones para crear y modificar arrays 97
o también
print (np.eye (4 ,3 ,1))
print (np.eye (4 ,3, -1))
[[ 0. 1. 0.]
[ 0. 0. 1.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
[[ 0. 0. 0.]
[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
[ 1. 1. 1.]
[ [ 1 0 0]
[0 2 0]
[0 0 3 ] ]
[ [ 0 0 0]
[1 0 0]
[0 2 0 ] ]
[[0 0 2 0]
[0 0 0 3]
[0 0 0 0]
[0 0 0 0]]
[ [ 1 3]
[8 4 ] ]
4 3
SLICING
Al igual que con las listas, podemos acceder a los elementos de un array mediante
slicing, que en NumPy además admite más sofisticación. Por ejemplo, la asignación
sobre un trozo de una lista funciona de diferente modo que en los arrays:
[0 , 6 , 4]
[0 6 6 6 4]
[ [ 0 1 2]
[3 4 5]
[6 7 8 ] ]
[3 4 5]
[6 7 8]
[2 5 8]
Nótese que no hay diferencia en la salida de los dos últimos resultados: ambos son
arrays unidimensionles. En la sección 4.4.1 trataremos este hecho con más detalle.
[ 0 , 1 , 2 , 3 , −1]
[2 , 3 , 4]
Sin embargo,
[2 3 4]
[ 0 1 2 3 −1]
[ 2 3 −1]
¿Qué ventajas puede tener este extraño comportamiento? Si por ejemplo estamos
trabajando con un array y con su transpuesto, y en algún momento hemos de modi-
ficar el array original, tiene sentido que también queramos realizar la modificación
en su transpuesto. Gracias a este comportamiento, no es necesario realizar explícita-
mente la modificación, pues ambos objetos, el array y su transpuesto están enlazados
de forma permanente (es decir, el transpuesto es una vista, esto es, otra forma de
ver el objeto original).
¿Cómo podemos entonces modificar un array, pero no su transpuesto? Dispone-
mos para ello del método copy que nos permite realizar copias del objeto, que ya no
tienen el comportamiento de las vistas:
100 Capítulo 4 NumPy
[[ −1 1 2]
[ 3 4 5]
[ 6 7 8]]
[ [ 0 1]
[3 4 ] ]
Es importante señalar que los métodos reshape y transpose crean vistas del
objeto, mientras que flatten crea una copia.
4 4
OPERACIONES
Cuando usamos operadores matemáticos con arrays, las operaciones son llevadas
a cabo elemento a elemento:
print (a+b)
print (a*b)
print (a/b)
print (b**a)
[[ 1. 2.]
[ 3. 4.]]
[[ 5. 6.]
[ 7. 8.]]
[[ 5. 12.]
[ 21. 32.]]
[ [ 19. 22.]
[ 43. 50.]]
print (a @ b)
[ [ 19. 22.]
[ 43. 50.]]
print (np.sin(a))
[ [ 0.84147098 0 . 9 0 9 2 9 7 4 3 ]
[ 0.14112001 −0.7568025 ] ]
print (np.exp(b))
[ [ 148.4131591 403.42879349]
[ 1096.63315843 2980.95798704]]
Nótese que sin y exp son funciones matemáticas definidas en el módulo NumPy
(entre otras muchas). Sin embargo, usar la función seno del módulo math:
102 Capítulo 4 NumPy
import math
math.sin(a)
import math
def myfunc (a):
return math.sin(a)
entonces, podemos usarla para trabajar sobre arrays del siguiente modo:
podemos evaluar lo que tarda en calcular la función vectorizada vfunc del siguiente
modo:
% %timeit
b = vfunc (a)
% %timeit
b = np.sin(a)
3 Por ello, es fundamental no incluir órdenes de impresión en una celda con % %timeit.
4.4 Operaciones 103
[0 1 2]
(3 ,)
print (a.T)
print (a.T.shape)
[0 1 2]
(3 ,)
a.T @ a
a @ a.T
a @ a
Ello es debido a que tanto a como a.T son arrays unidimensionales, que son objetos
distintos a las matrices.5
4 Existe además la función outer para el producto tensorial de arrays unidimensionales
(vectores) así como inner y cross para el producto interior y el producto vectorial.
5 El módulo numpy también dispone de un tipo de objeto matrix, pero no lo vamos a usar
en este texto.
104 Capítulo 4 NumPy
b = a. reshape (1 ,3)
print (b.T @ b)
[ [ 0 0 0]
[0 1 2]
[0 2 4 ] ]
print (b @ b.T)
[[5]]
Existe una forma alternativa un poco más sofisticada (pero más útil, pues no es
necesario conocer las dimensiones precisas para poder usar reshape), con la orden
newaxis, que crea una nueva dimensión en el array:
c = a[np. newaxis ]
print (c.T @ c)
[ [ 0 0 0]
[0 1 2]
[0 2 4 ] ]
Obsérvese que
(1 , 3)
4 4 2 Álgebra Lineal
El módulo NumPy posee un submódulo, linalg para realizar operaciones ma-
temáticas comunes en Álgebra Lineal como el cálculo de determinantes, inversas,
autovalores y solución de sistemas. Vemos unos ejemplos a continuación:
[ [ 1. 4. 4.]
[ 0. 0. 2.]
[ 2. 0. 0.]]
16.0
donde hay que tener en cuenta que los autovectores corresponden a las columnas de
vec.
Para el cálculo de la matriz inversa:
[ [ 0. 0. 0.5 ]
[ 0.25 −0.5 −0.125]
[ 0. 0.5 0. ]]
[ 2. 3. 1.]
Comprobamos:
print (a @ x - b)
[ 0. 0. 0.]
106 Capítulo 4 NumPy
4 4 3 Concatenación
[ 0 1 2 3 4 10 11 12 13 1 4 ]
Si lo que queremos es unir los arrays 1D en una matriz de dos filas debemos
considerarlos como matrices añadiéndoles una dimensión:
[ [ 0 1 2 3 4]
[ 1 0 11 12 13 1 4 ] ]
[ [ 0 1 2]
[3 4 5 ] ]
[ [ 1 0 11 1 2 ]
[ 1 3 14 1 5 ] ]
[ [ 0 1 2]
[ 3 4 5]
[ 1 0 11 1 2 ]
[ 1 3 14 1 5 ] ]
Para hacerlo por columnas es preciso añadir la opción axis=1 (por defecto es axis=0)
[[ 0 1 2 10 11 1 2 ]
[ 3 4 5 13 14 1 5 ] ]
4.5 Broadcasting 107
4 5
BROADCASTING
print (a+1)
[ 2. 3. 4.]
[ [ 1. 2.]
[ 3. 4 . ]
[ 5. 6.]]
[ −1. 3 . ]
print (a+b)
[ [ 0. 5.]
[ 2. 7.]
[ 4. 9.]]
[ [ 0. 1.]
[ 2. 3.]]
[ 1. 2 . ]
[ [ 1. 3.]
[ 3. 5.]]
[ [ 1. 2.]
[ 4. 5.]]
o alternativamente,
[ [ 1. 2.]
[ 4. 5.]]
(2 , 2) ( 2 , )
Nótese que b es esencialmente una matriz fila (un array unidimensional), por lo
que a+b es equivalente a
! ! !
0 1 1 2 1 3
+ =
2 3 1 2 3 5
(2 ,2) (2 , 1)
4.5 Broadcasting 109
[ [ 2 4]
[14 2 8 ] ]
[ [ 2 4]
[14 2 8 ] ]
[ [ 1 8 24]
[54 7 2 ] ]
[ [ 50 6 0 ]
[110 1 3 2 ] ]
110 Capítulo 4 NumPy
[ [ [ 1]
[ 7]]
[ [ 3]
[ 9]]
[ [ 5]
[11]]]
(3 , 2 , 1)
[[[ 2 4]]
[[ 6 8]]
[[10 12]]]
(3 , 1 , 2)
Así,
[[[ 2 4]
[ 14 28]]
[ [ 18 24]
[ 54 72]]
[ [ 50 6 0 ]
[110 1 3 2 ] ] ]
4 6
OTRAS OPERACIONES DE INTERÉS
[ 1 16 49 100 169]
335
13249600
169
[ 1 0 16 49 50 5 0 ]
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 1 1 ] ]
a.sum ()
66
[ 1 2 15 18 2 1 ]
[ 6 22 3 8 ]
4 6 1 Comparaciones
También es interesante la comparación entre arrays:
a == b
a > 2
[ True F a l s e True ]
True
False
print (a)
[ 1. 3. 0.]
a r r a y ( [ F a l s e , F a l s e , F a l s e ] , dtype = bool )
También existe la función where que crea un nuevo array a partir de una
comparación:
Como podemos ver, si el elemento del array no es cero, considera su inverso, en caso
contrario, lo hace igual a -1. Si usamos esta función sin los dos últimos argumentos,
114 Capítulo 4 NumPy
nos proporciona los índices en los que la condición es cierta (nótese que no es
necesario escribir a!=0, sino simplemente a):
np. where(a)
( array ( [ 0 , 1 , 3 ]) ,)
print (np.sqrt(a))
[ nan 0. 1.]
[ F a l s e True F a l s e ]
[ True F a l s e F a l s e ]
4 7
INDEXACIÓN SOFISTICADA
Además del slicing que hemos visto, los arrays de NumPy se pueden indexar a
través de arrays con enteros o variables booleanas, denominados máscaras. Veamos
algunos ejemplos:
[11 9 12 15 11 7]
mask = (a %3 == 0) # múltiplos de 3
print (mask)
[ 9 12 1 5 ]
( array ( [ 1 , 2 , 3 ]) ,)
[ 1 1 −1 −1 −1 11 7]
La máscara también puede estar formada por enteros que representan índices:
[2 4 3 2 0]
[11 9 12 15 11 7]
[ 1 2 11 15 12 1 1 ]
[11 0 12 0 11 7]
[9 8 7 6 5 4 3 2 1 0]
[ [ 1 1]
[0 6 ] ]
[ [ 8 8]
[9 3 ] ]
Sin embargo, cuando el array es multidimensional, hay que tener más cuidado
con la indexación. Obsérvese el siguiente ejemplo:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 1 1 ] ]
[ 8 11]
Si queremos acceder a las filas segunda y tercera, con las mismas columnas, podemos
usar el slicing:
[ [ 4 7]
[ 8 11]]
[ 4 11]
[ [ 4 7]
[ 8 11]]
4 8
UN EJEMPLO DEL USO DEL SLICING
4 9
LECTURA DE FICHEROS
2 1
4 3
6 5
8 7
10 9
[[ 2. 1.]
[ 4. 3.]
[ 6. 5.]
[ 8. 7.]
[ 10. 9.]]
De forma similar tenemos la orden savetxt para guardar arrays de forma rápida.
La siguiente orden
c = d**2
np. savetxt ('nuevo.dat ',c)
No obstante es importante resaltar que estos mecanismos no son los más eficientes
para manejar una gran cantidad de datos.
4 10
BÚSQUEDA DE INFORMACIÓN
np.info(np.dot)
...
aunque es necesario advertir que puede que dicho código no esté disponible para
algunas funciones.
Por último, cuando no concemos el nombre exacto de la función, o su posible
existencia, podemos realizar una búsqueda por concepto para ver qué funciones en
NumPy están relacionadas con ese tema. Por ejemplo, si estamos buscando algún
tipo de descomposición matricial y queremos ver qué posee NumPy, podemos usar
la función lookfor:
np. lookfor ('decomposition ')
matrix .
numpy . polynomial . Hermite . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . HermiteE . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . Laguerre . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . Legendre . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . Chebyshev . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . Polynomial . f i t
L e a s t s q u a r e s f i t to data .
numpy . polynomial . hermite . h e r m f i t
L e a s t s q u a r e s f i t o f Hermite s e r i e s to data .
numpy . polynomial . l a g u e r r e . l a g f i t
L e a s t s q u a r e s f i t o f Laguerre s e r i e s to data .
numpy . polynomial . l e g e n d r e . l e g f i t
L e a s t s q u a r e s f i t o f Legendre s e r i e s to data .
numpy . polynomial . chebyshev . c h e b f i t
L e a s t s q u a r e s f i t o f Chebyshev s e r i e s to data .
numpy . polynomial . hermite_e . h e r m e f i t
L e a s t s q u a r e s f i t o f Hermite s e r i e s to data .
numpy . polynomial . polynomial . p o l y f i t
Least − s q u a r e s f i t o f a polynomial to data .
que nos proporciona un listado de todas las funciones en las que aparece el término
decomposition.
4 11
ACELERACIÓN DE CÓDIGO
Hemos comentado al inicio del capítulo que para obtener un buen rendimiento
con NumPy es obligado evitar a toda costa el uso de bucles. Sin embargo, en
ocasiones es difícil eludir el uso de bucles. En esta sección mostraremos el uso del
módulo numba gracias al cual es posible obtener tiempos de ejecución próximos a los
que se obtendrían al compilar código em lenguaje C, o similar.
Veamos el funcionamiento a través del siguiente ejemplo. El fractal de Mandelbrot
es un objeto del plano complejo que se genera de forma muy sencilla. Dado un número
complejo cualquiera c, se construye la sucesión por recurrencia siguiente:
En función del número c escogido, esta sucesión puede estar acotada o no. Por
ejemplo, si c = 2, la sucesión que se genera es 0, 2, 6, 38, . . . que es claramente no
acotada; pero si c = −1, la sucesión queda 0, −1, 0, −1, . . . que sí está acotada. El
conjunto de Mandelbrot se define como el conjunto de números complejos c tales
que la sucesión anterior permanece acotada. Es decir, −1 es un punto que está en el
conjunto de Mandelbrot, pero 2 no pertenece a dicho conjunto.
4.11 Aceleración de código 121
for y in range(siz):
for x in range (siz):
c = complex (x/siz*xamp + xint [0],y/siz*yamp +
yint [0])
z = 0
for i in range(lim):
z = z**2 + c
if abs(z) > 2:
img[y,x] = False
break
else:
img[y,x] = True
return img
puntos = 2000
limite = 200
xint =( -2. ,1.)
yint =( -1.5 ,1.5)
y la propia llamada:
Una vez obtenida la matriz, podemos dibujar el array obtenido del siguiente modo:6
%matplotlib
import matplotlib . pyplot as plt
% %timeit -r 1 -n 1
img= mandelplot (puntos ,limite ,xint ,yint)
Nótese que la función consta de tres bucles anidados, los dos primeros recorriendo
una matriz de 2000 × 2000, y no hemos usado ninguna función de NumPy que pueda
acelerar el proceso.
Si bien es posible plantear el código para hacer uso de algunas funciones de
NumPy, no parece que puedan evitarse todos los bucles. En estos casos es cuando el
módulo numba nos proporciona un resultado sorprendente con un esfuerzo mínimo.
(Low Level Virtual Machine) que permite compilar el código Python obteniendo un
rendimiento similar al de C o FORTRAN. El uso de numba es bastante simple,
funcionando a través de lo que se denomina un decorador. Bastará con cargar
el módulo y escribir antes de la definición de la función el decorador; la función
mandelplot quedaría:
@jit
def mandelplot (siz , lim , xint , yint):
.
.
.
% %timeit -r 1 -n 1
img= mandelplot (puntos ,limite ,xint ,yint)
obtenemos un redimiento 30 veces más rápido. La función jit posee una serie de
opciones que en ocasiones permiten afinar más la optimización, pero que no vamos
a tratar en este texto.
4 12
EJERCICIOS
E4.2 Escribe una función tenga como argumento de entrada una matriz y de-
vuelva 1 si es simétrica, −1 si es antisimétrica y 0 en caso contrario.
E4.3 Crear una función para construir una matriz de tamaño n con una estructura
como la siguiente:
2 −1 0 0 ··· 0 0
−1 2 −1 0 · · · 0 0
. .. .. .. . . .. ..
.
. . . . . . .
0 0 0 0 ··· 2 −1
0 0 0 0 · · · −1 2
124 Capítulo 4 NumPy
E4.10 Crear una función para construir una matriz de tamaño n con una estruc-
tura como la siguiente:
1 2 3 ··· n−1 n
1 3 3 ··· n−1 n
1 ··· n−1
2 5 n
.
. .. .. .. ... ..
. . . . .
1 2 3 ··· 2n − 3 n
1 2 3 ··· n−1 2n − 1
E4.13 Construye una función para calcular, en función del tamaño, los autovalores
de las matrices del tipo siguiente y comprueba que su producto coincide con el
126 Capítulo 4 NumPy
determinante:
2 1 0 0 ··· 0
1 2 1 0 ··· 0
0 1 2 1 ··· 0
. .. .. .. .. ..
.. . . . . .
0 0 0 0 ··· 2
E4.14 Construye una función tal que, dado un array de longitud cualquiera,
construya el conocido como determinante de Vandermonde.
1 1 1 ··· 1
x1 x2 x3 ··· xn
x21 x22 x23 ··· x2n
.. .. .. .. ..
. . . . .
xn−1
1 xn−1
2 xn−1
3 ··· xn−1
n
¿Podrías escribir el código para calcular esta fórmula usando un solo bucle? ¿Y sin
bucles?
E4.15 Crea un array aleatorio de enteros entre 0 y 100 bidimensional, de tamaño
1000 × 1000. Cuenta el número de múltiplos de 5 que hay en cada fila y luego calcula
la media de los valores obtenidos. Repite el proceso 100 veces.
Indicación: usa el método mean.
E4.16 Considera un conjunto de n puntos uniformemente espaciados en el interva-
lo (0, 1), x0 < x1 < · · · < xn−1 . Dada una función u, sabemos que una aproximación
de la derivada segunda en un punto x viene dada por
u(x + h) − 2u(x) + u(x − h)
u′′ (x) ≈
h2
Para la función u(x) = sin(x), calcula los valores reales de su derivada segunda en
los puntos xi interiores del intervalo y compáralos con los valores obtenidos en la
aproximación.
E4.17 Considera una matriz de la forma
!
0 I
A=
K D
5 1
OPTIMIZACIÓN SIN RESTRICCIONES
Submódulo Descripción
cluster Algoritmos de agrupamiento
constants Constantes físicas y matemáticas
fftpack Rutinas para la Transformada Rápida de Fourier
integrate Integración de ecuaciones diferenciales ordinarias
interpolate Interpolación y splines regulares
io Entrada y salida de datos
linalg Álgebra lineal
ndimage Procesamiento de imágenes
odr Regresión ortogonal
optimize Optimización y cálculo de raíces
signal Procesamiento de señales
sparse Matrices dispersas
spatial Estructuras de datos espaciales
special Funciones especiales
stats Funciones estadísticas
X
N −1
f (x0 , . . . , xN −1 ) = 100(xi − x2i−1 )2 + (1 − xi−1 )2
i=1
de la cual se conoce que alcanza su mínimo en el punto (1, . . . , 1), siendo f (1, . . . , 1) =
0.
En primer lugar, crearemos una función que nos proporcione los valores de la
función de Rosenbrook:
un punto inicial a partir del cual poner en marcha el procedimiento; además, nosotros
proporcionamos algunas opciones adicionales en forma de diccionario:
import numpy as np
from scipy. optimize import minimize
siendo el resultado:
Optimization terminated s u c c e s s f u l l y .
Current f u n c t i o n v a l u e : 0.000000
I t e r a t i o n s : 26
Function e v a l u a t i o n s : 2870
print (opt)
resultando:
0.00000000 e +00 ,
0.00000000 e + 0 0 ] ,
[ −1.11668317 e −01 , −1.97386502 e −01 ,
−1.16436208 e −01 ,
−6.49484932 e −02 , −1.82185447 e −02 ,
2.81508404 e −03 ,
8.48222299 e − 0 3 ] ,
[ −6.41742238 e −10 , −1.70859402 e −09 ,
−2.51610081 e −09 ,
−6.56698002 e −09 , −1.31379941 e −08 ,
−2.62980032 e −08 ,
−5.15356896 e − 0 8 ] ] )
fun : 2.2378384321174616 e −21
message : ' Optimization�terminated�s u c c e s s f u l l y . '
nfev : 2870
n i t : 26
status : 0
s u c c e s s : True
x : array ( [ 1. , 1. , 1. , 1. , 1. , 1. , 1.])
print (opt.x)
[ 1. 1. 1. 1. 1. 1. 1.]
El método empleado en este ejemplo es una variante del conocido como método
de Powell, que es un método de direcciones conjugadas que no usa información sobre
la derivada de la función objetivo. De hecho, ni siquiera es preciso que la función
sea diferenciable.
El resto de métodos disponibles para la función minimize puede consultarse con
el comando info. Por ejemplo, está disponible el método del Gradiente Conjugado de
Polak-Ribière, que sí precisa información de la derivada de la función objetivo. Dicha
información puede venir dada por el usuario a través de una función, o puede ser
estimada numéricamente por el propio algoritmo. Estas dos opciones son controladas
por el parámetro jac, que puede ser tanto un valor booleano como una función. Si
jac es una función, entonces se entiende que ésta proporciona el valor del gradiente,
mientras que si es un valor booleano y es True entonces significa que el gradiente de
la función objetivo es devuelto por la propia función objetivo. Si jac=False entonces
el gradiente se estimará numéricamente.
En el ejemplo anterior, dado que la derivada de la función de Rosenbrook es:
∂f
= −400x0 (x1 − x20 ) − 2(1 − x0 )
∂x0
∂f
= 200(xj − x2j−1 ) − 400xj (xj+1 − x2j ) − 2(1 − xj ), 1 ≤ j ≤ N − 2
∂xj
∂f
= 200(xN −1 − x2N −2 )
∂xN −1
5.1 Optimización sin restricciones 133
o bien hacer que la propia función objetivo devuelva también el gradiente, esto es,
definiríamos la función de la forma siguiente:
Optimization terminated s u c c e s s f u l l y .
Current f u n c t i o n v a l u e : 0.000000
I t e r a t i o n s : 103
Function e v a l u a t i o n s : 205
Gr ad ien t e v a l u a t i o n s : 205
Optimization terminated s u c c e s s f u l l y .
Current f u n c t i o n v a l u e : 0.000000
I t e r a t i o n s : 103
Function e v a l u a t i o n s : 205
Gr ad i en t e v a l u a t i o n s : 205
Optimization terminated s u c c e s s f u l l y .
Current f u n c t i o n v a l u e : 0.000000
I t e r a t i o n s : 146
Function e v a l u a t i o n s : 2466
Gr ad i en t e v a l u a t i o n s : 274
print (opt3.x)
5 2
OPTIMIZACIÓN CON RESTRICCIONES
Minimizar f (x)
sujeto a hi (x) = 0, 1 ≤ i ≤ n,
gj (x) ≥ 0, 1 ≤ j ≤ m,
lk ≤ xk ≤ uk , 1 ≤ k ≤ N,
Para resolver este tipo de problemas con el módulo optimize creamos en primer
lugar funciones para la función objetivo y su derivada:
def fobj(x):
return x [0]**2 + 2*x [1]**2 - 2*x[0]*x[1] - 2*x[0]
print (res)
fun : −1.0000001831052137
j a c : a r r a y ( [ − 1 . 9 9 9 9 9 9 8 2 , 1.99999982 , 0 . ])
message : ' Optimization terminated s u c c e s s f u l l y . '
nfev : 14
nit : 9
njev : 9
status : 0
s u c c e s s : True
x : a r r a y ( [ 1.00000009 , 1 . ])
5 3
INTERPOLACIÓN DE DATOS
(0.00, 0.00), (0.20, 0.56), (0.40, 0.93), (0.60, 0.97), (0.80, 0.68), (1.00, 0.14)
f = interp1d (x,y)
print (f(x))
print (f (0.23) )
0.6155
1 1
0.5 0.5
0 0
0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1
(a) Puntos a interpolar (b) Función interpolada
y obtener así una interpolación basada en splines de tercer orden. Otras opciones
son 'slinear' o 'quadratic' para interpolación por splines de primer o segundo
orden, respectivamente. La figura 5.2a muestra la interpolación cúbica.
Por último, disponemos de la función lagrange para calcular el polinomio de
interpolación de Lagrange de un conjunto de puntos. En este caso, la función
devuelve un objeto tipo polinomio de NumPy (que puede considerarse una función).
La figura 5.2b muestra un gráfico de dicho polinomio.
5 4 3 2
−1.563 x + 6.771 x − 9.479 x + 1.604 x + 2.807 x
138 Capítulo 5 SciPy
1 1
0.5 0.5
0 0
0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1
(a) Spline cúbico (b) Interpolación de Lagrange
5 4
RESOLUCIÓN DE ECUACIONES DIFERENCIALES
y ′′ + γy ′ + ωy = 0
omega = 2.
gamma = 0.5
y0 = [2.5 , 0] # condiciones iniciales
5 5
EJERCICIOS
Maximizar xexy
sujeto a x2 + y = 0
E5.4 Para la función f del ejercicio E5.3, construye la interpolación lineal a trozos
y mediante splines cúbicos en los puntos de abcisa 1, 2, 3 y 4. ¿Cuánto valen los
interpoladores en el punto 2.5? ¿Y en 0?
E5.5 Resolver el siguiente problema (ecuación de Airy):
y ′′ − xy = 0
1 1
y(0) = √ , y ′ (0) = − √
3 2
3 Γ( 32 )
3
3Γ( 13 )
donde Γ(x) denota la función Gamma de Euler. La solucion exacta de esta ecuación
es la función de Airy y(x) = Ai(x).
Indicación:. Las funciones Ai y Γ están en el submódulo special.
6 Gráficos con Matplotlib
El Backend
%matplotlib notebook
141
142 Capítulo 6 Gráficos con Matplotlib
mientras que si queremos que los gráficos aparezcan en una ventana propia bastará
poner
%matplotlib
que nos informa del backend seleccionado. Para manejar gráficos de forma interactiva
sugerimos usar esta segunda opción. Es importante señalar que no podemos cambiar
el backend una vez elegido a menos que reiniciemos el núcleo del entorno Jupyter.
6 1
GRÁFICOS INTERACTIVOS
Comenzaremos con el uso interactivo con el módulo pylab. La ventaja del uso
de este módulo es que carga tanto el módulo NumPy como las funciones apropiadas
de Matplotlib, pero lo hace de forma masiva, por lo que se tiende a usar otras
alternativas. No obstante, para construir gráficos sencillos es bastante cómodo.
6 1 1 Interactividad
Una vez cargado el módulo pylab, lo primero que haremos será saber si la sesión
es o no interactiva, para lo cual usamos la función:
True
figure ()
creará una nueva ventana, como la mostrada en la figura 6.1. La salida de la función
nos informa del objeto creado y la dirección de memoria donde reside. Dado que esta
información no es relevante por ahora, prescindiremos en lo sucesivo de mostrarla.
Si por el contrario, la salida del la función isinteractive() es False, entonces la
orden figure() dará el mismo resultado, pero no se mostrará ventana alguna. Para
ver el gráfico será preciso invocar la orden show(). Esta es la diferencia fundamental
entre tener la sesión en interactivo o no. Cualquier cambio en una sesión interactiva
se ve reflejado al momento sobre la gráfica, mientras que si la sesión interactiva no
está activada habrá que usar la orden show() (para mostrar la ventana gráfica por
primera vez) o draw() para actualizar los cambios hechos al dibujo.
En cualquier caso, podemos activar o desactivar la interactividad mediante:
6.1 Gráficos interactivos 143
6 1 2 Figuras y gráficos
La orden figure() crea una ventana con título Figure más un número entero
que irá incrementándose sucesivamente. Es posible hacer la llamada de la forma
figure(num), bien para crear la figura con la numeración que deseemos, o bien, si
dicha figura existe, para hacerla activa. En lugar de un número entero es posible
pasar un string como argumento, que se convertirá en el título de la ventana creada.
La orden
ventanas.
144 Capítulo 6 Gráficos con Matplotlib
El resultado puede verse en la figura 6.3. Nótese que hemos usado la función arange
Podríamos haber hecho que el nuevo comando plot borrara el dibujo anterior,
en lugar de añadirse al existente. La función hold es la encargada de activar o
desactivar el estado de concurrencia, esto es, si los sucesivos dibujos se mostrarán
junto a los anteriores, o bien éstos serán borrados y sustituidos por el último. Se
puede cambiar de estado invocándola sin parámetro, o bien activarlo o desactivarlo
mediante hold(True) o hold(False), respectivamente. La función ishold() nos
proporciona el estado de concurrencia actual.
Para cerrar una ventana bastará usar la orden
Si lo que queremos es borrar los gráficos de la figura activa sin cerrar la ventana
disponemos de
6 1 3 Subplots
El posible tener varios ejes distintos en la misma ventana gráfica, para lo cual
usaremos la orden:
subplot (n,m,k)
subplot (234)
abre una ventana como la de la figura 6.4a y selecciona dicho eje como el eje activo.
Nótese que la figura es dividida en dos filas de tres columnas cada una, y se crean
(a) (b)
subplot (232)
entonces en la misma figura se crean unos ejes en la posición 2 (ver figura 6.4b), que
serán los nuevos ejes activos. ¿Qué ocurrirá si ahora escribimos subplot(211)? En
este caso, la estructura es sobrescrita y aparecen los ejes en la posición que muestra
la figura 6.5, siendo éste último el nuevo eje activo.
subplot (221)
subplot (222)
subplot (212)
x = linspace (0 ,1 ,10)
y = sin(x)
z = cos(x)
w = sqrt(x)
plot(x,w) # dibuja sobre el eje activo (212)
subplot (221) # nuevo eje activo (221)
plot(x,y) # dibuja sobre el eje activo (221)
subplot (222) # cambiamos de eje activo al (222)
plot(x,z) # dibuja sobre el eje activo (222)
6 1 4 Axes
Es posible organizar los ejes creados en una figura de forma no estructurada con
el comando axes:
axes ()
6.1 Gráficos interactivos 147
que crea unos ejes por defecto, equivalentes a hacer subplot(111). Si a continuación
escribimos:
3 Nótese que el posicionamiento de los ejes por defecto corresponde a las coordenadas
normalizadas [0.125,0.1,0.775,0.8].
148 Capítulo 6 Gráficos con Matplotlib
6 2
AÑADIENDO OPCIONES
x = linspace (0 ,1 ,30)
y = sqrt(x)
z = x**2
(a) plot(x,y,'r-o',x,z,'g:')
(b) plot(x,y,'m--s',x,z,'bx')
6 3
CONFIGURANDO VARIOS ELEMENTOS DEL GRÁFICO
Títulos y leyendas
title (cadena)
También es posible distinguir cada una de las líneas trazadas con plot mediante
una leyenda, usando una etiqueta definida por el argumento opcional label. La
leyenda se activa con el comando legend, que entre otros argumentos permite situar
la leyenda en posiciones predeterminadas con la opción loc, el estilo de fuente, etc.
Una vez más la ayuda del comando proporciona todas las posibilidades. La figura 6.9
muestra el resultado de las siguientes órdenes:
x = linspace (0 ,1 ,20)
y = x**2
z = x**3
plot(x,y, linewidth =2, label='$x ^2$',color =(0 ,1 ,0))
plot(x,z, linestyle ='dashed ',color =(1 ,0 ,1) ,label ='$x ^3$')
title ('Un par de funciones ',fontsize =14)
legend (loc =0)
Nótese que en las cadenas de caracteres que conforman las etiquetas para la
leyenda se ha usado notación LATEX. También se han usado otras opciones del
comando plot. La leyenda debe ser activada después de los comandos plot y
recogerá todas las etiquetas en función del orden.
Texto y anotaciones
las posibilidades de creación de gráficos sino que además se puede ver el código con
el que se generan.6 Puesto que este código usa extensivamente los objetos y métodos
del módulo, veremos en la siguiente sección cómo trabajar con éstos.
6 4
GRÁFICOS Y OBJETOS
En las secciones anteriores hemos visto cómo funciona el módulo pylabde forma
interactiva. Esta forma de trabajar es útil para probar ejemplos sencillos, pero desde
nuestro punto de vista, tenemos un mayor control de lo que sucede en un gráfico si
manejamos adecuadamente los objetos de los que está compuesto. En esencia, lo que
necesitamos es almacenar los objetos con los que construimos un gráfico en variables
(objetos), y usar los métodos que provee Python para irlos modificando.
Por otra parte, en lugar de trabajar con el módulo pylab, en esta sección
usaremos directamente pyplot y NumPy, los cuales importaremos de la forma
estándar:
Crearemos una figura, la cual asignamos a una variable para acceder adecuadamente
a los métodos disponibles.
Los objetos gráficos tienen su propia jerarquía, por ejemplo, en una figura podemos
incluir varios ejes (tal y como hacíamos con subplot):
Ahora la variable a es una lista que contiene dos objetos gráficos de tipo Line2D, y
b es un sólo objeto gráfico de este tipo. Obsérvese el uso de la coma para almacenar
el objeto gráfico y no la lista.7 Ahora podemos acceder a las diversas propiedades
de cada uno de los objetos gráficos usando métodos:
6 Sólo hay que tener en cuenta la versión que se tiene instalada de Matplotlib, y la versión
que se use en el ejemplo, pues en ocasiones algunas características no están cubiertas por
versiones inferiores.
7 Podríamos haber hecho también a, c = ax1.plot(x,y,x,z) para almacenar los
El resultado puede verse en la figura 6.12. Las distintas opciones para el objeto
Line2D pueden consultarse en la ayuda. Por supuesto, también se pueden emplear
las opciones del mismo modo que en la secciones anteriores.
Figura 6.12
Para tratar distintos elementos de un gráfico, también es posible usar la función
setp que asigna una (o varias) propiedades a un objeto. Por ejemplo,
haría que las dos curvas de eje superior tengan ambas grosor 3, y que el eje inferior
luzca un título. Las mismas propiedades que hemos modificado en los ejemplos
anteriores pueden modificarse con esta orden.
f = plt. figure ()
ax = f. add_subplot (111)
b = ax.plot(x,y)
plt.setp(b, linewidth =2, color ='red ') # propiedades de la
curva
# banda de resaltado
band = ax. axvspan (2* np.pi /5 ,3* np.pi /5)
band. set_color ('red ') # ponemos color
band. set_alpha (0.2) # ponemos transparencia
para la que basta precisar el nombre del fichero a guardar y el formato del mismo.8
8 También podemos salvar los gráficos tanto desde la ventana gráfica como desde el gráfico
156 Capítulo 6 Gráficos con Matplotlib
Figura 6.13
6 5
GRÁFICOS 3D
Aunque la librería Matplotlib fue diseñada en principio para trabajar con gráficos
bidimensionales también incorpora la posibilidad de realizar gráficos 3D, aunque
hemos de señalar que existen otras alternativas interesantes como MayaVi.
Para usar gráficos 3D con Matplotlib precisamos además realizar la siguiente
importación:
Podemos dibujar curvas con el método plot vinculado a este tipo de ejes, usando
tres listas o arreglos que proporcionan las coordenadas de los puntos de la curva.
Por ejemplo,
p
f (x, y) = sin 2π x2 + y 2
El parámetro zdir señala el eje sobre el que se dibujan los contornos, mientras que
offset señala el nivel en el que se muestran (si este parámetro no aparece, se dibuja
cada contorno en su nivel correspondiente). Nótese la selección de transparencia
sobre el objeto cset con el parámetro alpha.
6 6
EJERCICIOS
Indicación: la función minimize_scalar usa una lista a modo de intervalo para acotar
el mínimo, aunque no asegura que el mínimo encontrado caiga en dicho intervalo.
Usa intervalos adecuados para localizar los máximos y mínimos buscados.
E6.4 Reproducir de la forma más aproximada los gráficos de la figura E6.4.
E6.5 Dibujar las siguientes funciones en los recintos indicados:
2
−y 2
(a) f (x, y) = e−x en [−2, 2]2 .
2
(b) f (x, y) = ex (x4 + y 4 ) en [−1, 1]2 .
p
(c) El cono f (x, y) = x2 + y 2 sobre el círculo unidad. Usar coordenadas polares.
(d) La superficie en polares z = (r2 − 1)2 sobre el círculo de centro origen y radio
1.25.
(e) La esfera unidad y la esfera de radio dos. Usar transparencia.
E6.6 Considera los puntos (0, 0), (1, 3), (2, −1), (3, 2), (4, 2) y, (5, −1). Dibújalos
usando triángulos de color verde. A continuación, calcula la función interpoladora
lineal, el spline cúbico y el polinomio interpolador de Lagrange. Dibuja cada uno de
ellos en un color distinto y etiquétalos para que aparezca una leyenda.
E6.7 Considera el siguiente código que genera tres líneas l1 , l2 y l3 :
160 Capítulo 6 Gráficos con Matplotlib
(c) 100 ptos. en el círculo unida�d (d) 100 ptos. en el círculo unidad
El módulo SymPy es una librería escrita en Python para realizar cálculo simbó-
lico que se ha convertido en una alternativa muy eficiente a otros CAS (Computer
Algebraic System) como pueden ser Maple o Mathematica. En estas notas usaremos
la versión 1.1.1.
7 1
VARIABLES SIMBÓLICAS
init_printing ()
ción matemática que permite su visualización inmediata en navegadores web, entre otras
aplicaciones.
163
164 Capítulo 7 SymPy
x + y 2 − log(z)
La función symbols admite como parámetro una cadena de caracteres separada
por espacios o comas y asigna cada una de ellas a una variable. Así, en la entrada
anterior se han creado tres variables simbólicas x, y, z asociadas a las expresiones
x, y y z. Es importante señalar que las variables asociadas nada tienen que ver con
el nombre asignado.
(α, x1 , a, hola)
En el ejemplo anterior hemos asignado la variable simbólica a a la expresión α, la cual
es interpretada por SymPy mediante su correspondiente símbolo griego. Del mismo
modo, la variable b representa a la expresión x1 , mientras que c se ha asignado a
a, lo cual es confuso, pero válido. Por último, la variable simbólica d hace referencia
a una expresión con un nombre arbitrario. Obviamente se recomienda asignar a
cada expresión una variable que la represente de forma precisa sin crear confusión.
También es importante señalar que son las variables simbólicas, esto es, la salida de
la función symbols, las que debemos manipular a la hora de realizar los cálculos:
α3 + x21 + ea
alpha + 1
(a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 , a9 )
En el entorno Jupyter, si en lugar de a escribimos print(a) no obtenemos la
salida formateada con MathML sino en formato habitual, lo cual puede ser un
engorro si queremos imprimir varias salidas bien formateadas en una misma celda.
Si en lugar de print usamos display obtendremos la salida deseada.
7.1 Variables simbólicas 165
7 1 1 Hipótesis
La función symbols permite usar un parámetro adicional con el que imponer
hipótesis sobre las variables creadas. Por ejemplo, las siguientes líneas definen una
variable entera y una real
Si ahora escribimos
1
observamos que, en efecto, el coseno de un múltiplo entero de 2π es 1. Sin embargo,
cos(2πx)
no es posible obtener una simplificación de la expresión cos(2πx) si x es real.
Las hipótesis posibles son commutative, complex, imaginary, real, integer,
odd, even, prime, composite, zero, nonzero, rational, irrational„ algebraic,
transcendental, finite, infinite, negative, nonnegative, positive,
nonpositive, hermitian, antihermitian.
Podemos preguntar si una variable es de alguno de estos tipos con el método
is_tipo, teniendo en cuenta que todas las variables pertenecen al mayor conjunto
posible. Por ejemplo,
True
False
None
La respuesta puede ser cierta, falsa, o bien None, que equivale a que no se puede
saber a priori. Más adelante veremos cómo usar las hipótesis para ayudar en las
simplificaciones.
7 1 2 Sustituciones
Otro aspecto a tener en cuenta es la distinción entre variables de Python y
variables simbólicas. Por ejemplo,
x = symbols ('x')
expr = x + 1
x = 2
expr
166 Capítulo 7 SymPy
x+1
Al cambiar el valor de la variable x a 2, ésta deja de ser una variable simbólica, pero
la expresión simbólica expr permanece inalterada. Si lo que pretendemos es obtener
el valor de una expresión al sustituir una variable simbólica por un determinado
valor hemos de usar el método subs:
41
que tiene como argumento un diccionario en el que asignamos los valores correspon-
dientes. Si queremos sustituir un único valor, no es necesario el diccionario
expr.subs(a ,3)
b4 + 9
La sustitución también puede hacerse sobre cualquier expresión simbólica:
expr.subs(a,b**2)
2b4
7 1 3 Manejo de números
SymPy permite manejar números reales con precisión arbitraria y realizar cálcu-
los exactos, pero hay que tener la precaución de definir correctamente los valores.
Para manejar números enteros disponemos de la función Integer
1
3
que podemos escribir abreviadamente como
Integer (1) /3
para obtener el mismo resultado. Otra alternativa para manejar números racionales
nos la da la función Rational. Así,
Rational (1 ,3) + 1
4
3
Hay que ser cuidadoso con las expresiones numéricas en Python o SymPy, pues
print (1/3)
print ( Integer (1/3) )
7.1 Variables simbólicas 167
0.3333333333333333
0
son diferentes debido a que en el primer caso no estamos usando SymPy, de ahí
que la respuesta sea el float que proporciona Python tal cual, mientras que en el
segundo caso se ha aplicado la función Integer al número real, de ahí que se obtenga
su parte entera. Del mismo modo,
cos (0.666666666666667 * p i )
−1/2
print (2**(1/2) )
print (Pow (2 ,1/2))
Pow (2, Integer (1) /2)
1.4142135623730951
1.41421356237310
√
2
Dado que la función Pow de SymPy es equivalente a la potencia, los dos últimos
resultados deberían ser los mismos, y de hecho lo son:
2.0000000000000004
2.00000000000000
2
En el primer caso, dado que 2**(1/2) es un float, el resultado no es exactamente 2
debido a los errores de redondeo. No
√ es así en los otros dos casos, pues en ambos se
está usando la expresión exacta de 2; lo que ocurre es que Pow(2,1/2) proporciona
una expresión decimal, mientras que la última es una expresión simbólica.
1.4142135623731
mientras que si llamamos con un parámetro entero el resultado aparecerá con el
número de cifras decimales precisado con el parámetro
1.4142135623730950488016887242096980785696718753769
Para evaluar numéricamente una expresión con sustitución, disponemos de un
parámetro subs que funciona de forma muy similar al método del mismo nombre:
4.11111111111111
Existen dos expresiones equivalentes para el método evalf: el método n y la
función N, de manera que las siguientes expresiones son idénticas
1+3*I
4-I
7 2
SIMPLIFICACIÓN
1
7.2 Simplificación 169
x−1
El problema es que a veces no es fácil determinar qué se entiende por la forma
más sencilla de una expresión. Por ejemplo, podríamos querer obtener la siguiente
expresión
(x − 2)(x − 3) = x2 − 5x + 6
(x − 3)(x − 2)
x2 − 5x + 6
pero como podemos observar, las simplificaciones no ha tenido efecto. Es aquí donde
debemos usar otras funciones más específicas:
x2 − 5x + 6
(x − 3)(x − 2)
Estas funciones no son exclusivas de polinomios:
sin3 (x) + 6 sin2 (x) cos (x) + 12 sin (x) cos2 (x) + 8 cos3 (x)
x2 (y − 1) + x (−4y + 4) + 4y − 4
mientras que cancel simplifica factores comunes en expresiones racionales, de forma
similar a factor, aunque la salida es ligeramente diferente:
1
y 2 − 2yz + z 2
x−1
170 Capítulo 7 SymPy
factor (expr)
(y − z)2
x−1
Para expresiones racionales, también es muy útil la función apart que realiza
una descomposición en suma de fracciones irreducibles:
10 3
x−5+ −
x+1 (x + 1)2
cos (2x)
powsimp (x**a*x**b)
xa+b
Sin embargo, hay que tener en cuenta que ciertas simplificaciones sólo son correctas
bajo hipótesis concretas sobre las variables. Por ejemplo, es bien conocido que
xa y a = (xy)a
powsimp (x**a*y**a)
xa y a
no realiza la simplificación, pues como hemos visto, no es cierta en general. Pero si
imponemos las hipótesis adecuadas,
(xy)a
7.3 Resolución de ecuaciones algebraicas 171
Si no queremos tener que lidiar con las hipótesis pertinentes, podemos usar el
parámetro force:
xa y a
7 2 2 Identidades
En SymPy, el signo == no representa una igualdad simbólica, es decir,
False
a = (x+1) **2
b = x**2 + 2*x +1
Eq(a,b). simplify ()
True
simplify (a-b)
7 3
RESOLUCIÓN DE ECUACIONES ALGEBRAICAS
alternativas.
La función solveset permite resolver ecuaciones algebraicas de cualquier tipo.
Su sintaxis es sencilla, debemos proporcionar una ecuación y la variable que quere-
mos resolver. Por ejemplo, para resolver la ecuación x3 = 1 escribiremos
{(−2z − 2, z + 3, z)}
Si solveset no es capaz de encontrar solución devuelve un conjunto condicional
solveset (cos(x)-x,x)
{x | x ∈ C ∧ −x + cos (x) = 0}
que no hay que confundir con el caso en el que no hay solución
solveset (exp(x),x)
∅
en cuyo caso devuelve el conjunto vacío.
7 4
ÁLGEBRA MATRICIAL
B.T * A
h i
x−1 1 −y + 1
Los operadores aritméticos son los habituales y se puede calcular la inversa mediante
A** -1
2 2
0
−4y−2
2x+4 2x+4
x(2y+1)−x−2 2x+4
−2x−4 −2x−4 −2x−4
2
x+2
− x+2
x
0
o el determinante:
A.det ()
−x − 2
174 Capítulo 7 SymPy
Hay aspectos en los que el manejo se hace más sencillo que con NumPy, como
la extracción de filas o columnas
A.row (1)
h i
2 0 −1
A.col (0)
x
2
1
la insercción de nuevas filas o columnas
A. col_insert (1,B)
x 1 x 0
2 0 2 0
1 −1 1 −1
o su eliminación
A. row_del (2)
A
" #
x 0 1
2 0 −1
Nótese que este último método no devuelve nada, pero la matriz es modificada
internamente, a diferencia de los anteriores, que retornan la nueva matriz, pero no
modifican la original.
7 4 1 Construcción de matrices
A = eye (3)
B = zeros (2)
C = ones (3 ,2)
A, B, C
1 0 0 " # 1 1
0 0
0 0 1 1
1 , 0 0
,
0 0 1 1 1
aunque el uso puede ser ligeramente distinto. Por ejemplo, con la función diag
7.4 Álgebra Matricial 175
diag (1 ,2 ,3)
1 0 0
0 2 0
0 0 3
7 4 2 Álgebra lineal
SymPy también dispone de algunas funciones para obtener el núcleo de una
matriz
for x in k:
print(A*x)
Matrix ( [ [ 0 ] , [ 0 ] ] )
Matrix ( [ [ 0 ] , [ 0 ] ] )
Matrix ( [ [ 0 ] , [ 0 ] ] )
A. columnspace ()
"" # " ##
3 −1
,
1 0
o el rango de la misma
176 Capítulo 7 SymPy
A.rank ()
2
Sin embargo, si la matriz contiene símbolos indefinidos no se realiza un estudio
en función de los mismos, por lo que los resultados no son correctos. Por ejemplo,
3
Pero si x = −2,
A.subs(x, -2).rank ()
{1 : 3, 2 : 1}
Como vemos, los autovalores vienen en forma de diccionario. Esta matriz tiene
un autovalor 1 de multiplicidad 3 y otro autovalor 2 de multiplicidad 1. Para los
autovectores,
A. eigenvects ()
1 0 0
0 1 0
1, 3, , , 2, 1,
0 0 3
0 0 1
obtenemos una lista con los autovalores, multiplicidad y autovectores asociados a
cada autovalor. La forma de Jordan se obtiene:
A. jordan_form ()
2 0 1 0 1 1 0 0
−1 0 0 0 0 1 0 0
,
0 1 0 3 0 0 1 0
0 0 0 1 0 0 0 2
donde la primera de las matrices es la matriz de paso, y la segunda la forma de
Jordan.
7.5 Cálculo 177
7 5
CÁLCULO
7 5 1 Límites
SymPy puede calcular el límite de una función en un punto, inclusive en el
infinito:
x = symbols ('x')
limit (sin (3*x)/x,x ,0)
limit (x*tan(pi/x),x,oo)
π
Nótese el uso del símbolo oo para infinito. También es posible obtener límites
laterales
limit (1/x,x,0, dir="-")
−∞
Además, la función Limit nos proporcional el objeto sin evaluar
_.doit ()
2
Obsérvese que por defecto, el límite realizado es por la derecha.
7 5 2 Derivación
La derivación se lleva a cabo con la función diff:
x, y = symbols ('x y')
f = cos(x**2)
diff(f,x)
−2x sin x2
La función también puede ser usada como método, y se permite bastante flexibilidad
para indicar derivadas de orden superior. Por ejemplo, la derivada tercera
f.diff(x ,3)
4x 2x2 sin x2 − 3 cos x2
aunque también se puede escribir así:
178 Capítulo 7 SymPy
f.diff(x,x,x)
diff(sin(x*y),x,y,y)
Derivative (sin(x*y),x,y,y)
∂3
sin (xy)
∂x∂y 2
Al igual que antes, podemos evaluar con el método doit.
7 5 3 Integrales
integrate (cos(x),x)
sin(x)
como definida, en la que debemos proporcionar una tupla con la variable de inte-
gración y los límites:
1
Como vemos, se pueden calcular integrales impropias, y con respecto a varias
variables:
π
La función Integral nos proporciona el objeto sin evaluar:
_.doit ()
atan (x)
7.6 Gráficos con SymPy 179
7 5 4 Series de Taylor
f = log (1+x)
f. series (x ,0 ,6)
x2 x3 x4 x5
x− + − + + O x6
2 3 4 5
y si no queremos que aparezca el término de orden:
_. removeO ()
x5 x4 x3 x2
− + − +x
5 4 3 2
Por supuestp, es posible cambiar el punto respecto del que realizar el desarrollo:
(x − 1)3 (x − 1)2 1
− + − (x − 1) + e−1
6e 2e e
7 6
GRÁFICOS CON SYMPY
Aunque ya conocemos las capacidades gráficas del módulo Matplotllib con el que
podemos representar todo tipo de funciones, el módulo SymPy nos da también la
posibilidad de representar funciones de manera más rápida y sencilla. Por ejemplo,
si queremos representar la función f (x) = log(x) con Matplotlib, habrá que ser
cuidadosos a la hora de elegir el array de abcisas, pues la función no está definida
para valores menores o iguales que cero. Con SymPy no es necesario prestar atención
a estos detalles. Bastará escribir
x = symbols ('x')
plot(log(x))
Por otro lado, con plot3d se pueden dibujar superficies en 3D (véase la figu-
ra 7.5):
182 Capítulo 7 SymPy
7 7
EJERCICIOS
x3 − y 3 √ √ √ √
= x2 + xy + y 2 x − y = ( x − y)( x + y)
x−y
Figura 7.7: Superficie ((2 − cos x) cos y, (2 − cos x), cos y, sin y) x, y ∈ (0, 2π)
√
2x + |x| x+1−1
lím lím √
x→0− 4x − |x| x→0 3
x+1−1
184 Capítulo 7 SymPy
sen x
E7.4 Comprobar que la derivada de f (x) = arctan 1+cos x
es una constante y
averiguar su valor.
E7.5 Dada la función f (x) = (1 + x)α , comprobar que
x2 − 5x + 9
x2 − 5x + 6
e integrar término a término. Luego comprobar que el resultado coincide con la
integral de la expresión completa.
8 Programación Orientada a Objetos
185
186 Capítulo 8 Programación Orientada a Objetos
5
2 3
3
1 4
2
0 1
0
mero de nodos que vamos a tener en cada dimensión y cuya salida fueran los arrays
de coordenadas y conexiones correspondientes.
No obstante, vamos a crear esta estructura mediante objetos. Inicialmente podrá
parecer que es más complicado proceder de este modo, pero más adelante veremos
que merece la pena diseñar la estructura así, pues nos facilitará la implementación
de nuevas posibilidades.
8 1
DEFINIENDO CLASES
class Point :
"""
describe un punto de coordenadas (x,y)
"""
def __init__ (self ,xx ,yy):
self.x = xx
self.y = yy
Las clases se definen con la palabra clave class seguida del nombre asignado a
la clase y que define el tipo de objeto, y como parámetros, los objetos de los cuales
hereda (veremos el concepto de herencia un poco más abajo). Es una convención
ampliamente usada nombrar las clases definidas por el usuario con la inicial en
mayúsculas. También es muy conveniente documentar adecuadamente la clase.
Como es habitual en Python, la sangría marcará el fragmento de código corres-
pondiente a la clase. A continuación, aunque en Python no es obligatorio, aparece
el denominado constructor. Se trata del método __init__ que se ejecuta cuando la
clase se instancia. El proceso de instanciación no es más que la definición de un
objeto perteneciente a esta clase.
Puesto que __init__ es un método, esto es, una función, se define como ya vimos
con la palabra clave def. Los argumentos de los métodos de una clase son un poco
peculiares pues el primero de ellos siempre es self, que se refiere al propio objeto.2
El resto de argumentos deberá aparecer en el momento de instanciar al objeto, y los
podemos entender como los argumentos de entrada en la creación del objeto.
De este modo, para definir un objeto punto, instanciamos su clase del siguiente
modo:
En principio no hay mucho más que hacer con un objeto de este tipo. Podemos
acceder a sus atributos,
p.x
2.0
p.y
3.0
o incluso modificarlos:
p.x = 5.
print (p)
2 Esto es un convenio universalmente aceptado pero podría usarse cualquier otro nombre.
188 Capítulo 8 Programación Orientada a Objetos
sólo obtenemos información sobre el mismo. Esto es debido a que no hemos especi-
ficado cómo imprimir adecuadamente el objeto. Para hacer esto se define el método
__str__ dentro de la clase:
(2.0 ,3.0)
El método __str__ es uno de los llamados métodos especiales, que están aso-
ciados al comportamiento de ciertas funciones en Python. En concreto, el método
__str__ es invocado cuando usamos la función print. Existen otros métodos de este
tipo útiles cuando manejamos otras funciones u operadores (véase el ejercicio E8.6).
class Nodo:
"""
describe un nodo mediante identificador y punto
"""
def __init__ (self ,n,a,b):
self.id = n
self.p = Point (a,b)
Entonces,
print (a.p)
(1.0 ,3.0)
b.p.x
1.0
8.1 Definiendo clases 189
Sin embargo, dado que hay una gran similitud entre los objetos tipo punto y los
objetos tipo nodo, otra opción consiste en apoyarse en el concepto de herencia, que
no es más que el establecimiento de una relación entre dos clases, de manera que
los atributos y métodos de una puedan ser usados en la otra. En nuestro caso es
evidente que los atributos x e y de la clase Point deberán mantenerse en la nueva
clase que vamos a crear, por lo que podemos aprovecharnos del constructor de la
clase Point usando el comando super:
(1.0 ,2.0)
a.id
b.id
Nótese que para la impresión del objeto Node se está usando el método __str__ de
la clase Point. Si quisiéramos una impresión distinta habría que definir nuevamente
el método __str__ para esta clase.
Ahora no debe ser difícil para el lector entender la clase para las barras siguiente:
190 Capítulo 8 Programación Orientada a Objetos
class Bar:
"""
define una barra soportada por dos nodos
"""
def __init__ (self ,n1 ,n2):
if n1.id == n2.id:
print("Error: no hay barra ")
return
elif n1.id < n2.id:
self.orig = n1
self.fin = n2
else:
self.orig = n2
self.fin = n1
def __str__ (self):
return " Barra de extremos los nodos {0} y
{1}". format (self.orig.id ,self.fin.id)
barra = Bar(a,b)
print (barra )
B a r r a de extremos l o s nodos 0 y 1
Con esto hemos definido los elementos esenciales que participan en la construc-
ción de una estructura de barras como la de la figura 8.1. Ahora vamos a construir la
estructura, que como comentamos al inicio, consta esencialmente de nodos y barras.
Los parámetros de entrada podrían ser dos puntos que determinen el rectángulo
sobre el que crear la estructura, junto con el número de nodos a usar en cada di-
mensión.
Una posibilidad vendría dada por el siguiente código:
class Truss :
"""
genera una estructura rectangular de barras
- nx: numero de nodos en abscisas .
- ny: numero de nodos en ordenadas .
- p1: vértice inferior izquierdo (clase punto).
- p2: vértice superior derecho ( clase punto ).
Node. numberNode = 0
# construcción de nodos
nodx = np. linspace (p1.x,p2.x,nx +1)
nody = np. linspace (p1.y,p2.y,ny +1)
for yy in nody:
for xx in nodx:
self.nodos. append (Node(xx ,yy))
# construcción de barras
for j in range (ny):
for i in range (nx):
n1 = i+ j*(nx +1)
n2 = n1 + 1
n3 = n1 + nx + 1
n4 = n3 + 1
# barras en cada elemento
b1 = Bar(self.nodos[n1],self.nodos[n2])
b2 = Bar(self.nodos[n1],self.nodos[n3])
b3 = Bar(self.nodos[n1],self.nodos[n4])
b4 = Bar(self.nodos[n2],self.nodos[n3])
self. barras . extend ([b1 ,b2 ,b3 ,b4])
# barras finales a la derecha
self. barras . append (Bar(self.nodos [n2],self.nodos[n4]))
Nótese que hemos definido un par de listas: nodos y barras en las que almacenar
los elementos que nos interesan. Ponemos el contador del identificador de nodos
a cero, de manera que cada vez que tengamos una estructura, los nodos se creen
comenzando con el identificador en 0. Tal y como está construido, el identificador de
cada nodo coincide con el índice que ocupa en la lista nodos, lo que nos simplifica
la búsqueda de los nodos.
Para obtener los nodos y las barras disponemos de las listas anteriores, pero
será más cómodo si definimos unos métodos que nos proporcionen directamente
192 Capítulo 8 Programación Orientada a Objetos
array ( [ [ 0. , 0.] ,
[ 1. , 0.] ,
[ 0. , 1.] ,
[ 1. , 1.]])
m. get_connection ()
array ( [ [ 0 , 1] ,
[0 , 2] ,
[0 , 3] ,
[1 , 2] ,
[1 , 3] ,
[2 , 3]])
Es evidente que podríamos haber creado una función que tuviera como entrada
las coordenadas de los puntos del rectángulo y el número de nodos a usar en cada
dimensión, y cuya salida fuera precisamente los dos arrays que hemos obtenido;
posiblemente hubiera sido incluso más sencillo de implementar. Sin embargo, como
ahora veremos, es más conveniente el uso de clases porque nos va a permitir una
flexibilidad aun mayor.
8 1 1 Añadiendo métodos
creada para añadirle la parte gráfica, o bien implementamos la parte gráfica en una
función aparte, que reciba los arrays que definen la estructura y los dibuje.
La primera opción puede resultar un engorro, pues cada vez que ejecutemos la
función obtendremos los arrays y el gráfico y habrá ocasiones en las que queramos
crear sólo la información de la estructura y otras en las que sólo queramos dibujar.
La segunda opción nos obliga a llamar primero a la función para obtener los arrays
de coordenadas y conexiones, y luego pasarlos a la nueva función para dibujar.
Sin embargo, implementar un nuevo método dentro de la clase para que constru-
ya el gráfico es mucho más cómodo, pues podremos invocarlo independientemente
de que construyamos o no los arrays de coordenadas y conexiones. Podríamos añadir
a la clase Truss algo así:
De este modo, una vez creada una estructura, nos bastará con invocar al método
plotting para obtener el gráfico correspondiente.
8 2
CONTROLANDO ENTRADAS Y SALIDAS
A=D+L+U
A =
np.array ([[10 , -1 ,2 ,0.] ,[ -1 ,11 , -1 ,3] ,[2 , -1 ,10 , -1] ,[0 ,3 , -1 ,8]])
b = np.array ([6. ,25 , -11 ,15])
iterativo (A,b)
I t e r a c i o n e s r e a l i z a d a s : 23
array ( [ 1. , 2. , −1. , 1 . ] )
I t e r a c i o n e s r e a l i z a d a s : 10
array ( [ 1. , 2. , −1. , 1 . ] )
class Resolucion :
def __init__ (self ,A,b, metodo = jacobi ):
self.A = A
self.b = b
self. metodo = metodo
self.eps = 1.e -8
def iteracion (self):
self.k = 0
M,c = self. metodo (self.A,self.b)
x0 = np. zeros (self.A. shape [0])
x = np. ones_like (x0)
while np. linalg .norm(x-x0) >
self.eps*np. linalg .norm(x0):
x0 = x.copy ()
x = np.dot(M,x0) + c
self.k += 1
return x
Podemos ver que la clase Resolucion tiene dos métodos: el constructor, que rea-
liza la inicialización de datos y el método iteracion que lleva a cabo las iteraciones,
de igual modo que antes. A diferencia del código anterior, aquí no se imprime el
número de iteraciones. Para ejecutar ahora el algoritmo mediante la clase anterior
escribiremos:
a = Resolucion (A,b)
a. iteracion ()
a.k
23
a. metodo = seidel
a. iteracion ()
a.k
10
a.eps =1.e -4
a. iteracion ()
a.k
3 Nótese el uso del atributo __name__ para una función, que nos devuelve su nombre.
198 Capítulo 8 Programación Orientada a Objetos
jacobi
Precisión : 1e −02 −−− Iteraciones : 7
Precisión : 1e −04 −−− Iteraciones : 12
Precisión : 1e −06 −−− Iteraciones : 18
Precisión : 1e −08 −−− Iteraciones : 23
Precisión : 1e −10 −−− Iteraciones : 28
Precisión : 1e −12 −−− Iteraciones : 34
seidel
Precisión : 1e −02 −−− Iteraciones : 3
Precisión : 1e −04 −−− Iteraciones : 6
Precisión : 1e −06 −−− Iteraciones : 8
Precisión : 1e −08 −−− Iteraciones : 10
Precisión : 1e −10 −−− Iteraciones : 11
Precisión : 1e −12 −−− Iteraciones : 13
Obviamente podríamos haber hecho algo similar con la función definiendo oportuna-
mente los parámetros de entrada para dotarla de más flexibilidad, pero una vez más
tendríamos que modificar el código de la misma. Éste es precisamente el hecho que
queremos resaltar en cuanto a la ventaja de usar clases en lugar de funciones para
la programación científica: si diseñamos adecuadamente los atributos y métodos de
la clase, disponemos de acceso completo a los mismos, tanto para introducir como
para extraer datos. No sólo eso; además, las modificaciones que realicemos sobre la
clase no tienen por qué afectar al constructor, por lo que podemos seguir usándola
del mismo modo sin necesidad de mantener diversas versiones del mismo código. En
conclusión, las clases son mucho más fáciles de mantener y actualizar.
8 3
EJERCICIOS
E8.2 Añade un nuevo método long a la clase Bar de manera que barra.long()
devuelva la longitud de barra.
E8.3 Para la clase Truss de la sección 8.1, escribir un método para que el comando
print proporcione información sobre el número de nodos y el número de barras de
la estructura.
E8.4 Usando el método long definido en el ejercicio E8.2, añadir un método a la
clase Truss que proporcione la longitud total de todas las barras de la estructura.
8.3 Ejercicios 199
E8.5 Define una clase que contenga dos métodos: getString con el que obtener
una cadena de texto introducida por teclado y printString que imprima la cadena
obtenida en mayúsculas, dejando un espacio de separación entre cada letra. Debería
funcionar del siguiente modo:
a = InputOutString ()
a. getString ()
a. printString ()
H O L A
E8.6 Definir una clase para trabajar con números racionales, de manera que los
objetos se creen del siguiente modo:
a = Rational (10 ,3)
Implementa los siguientes métodos
reduce: para que el objeto creado sea convertido a una fracción irreducible.
Habrá que calcular el máximo común divisor de ambos números (véase ejer-
cicio E2.10 del Capítulo 2) y dividir entre él. Este método se debe llamar en
el constructor para modificar el objeto introducido inicialmente.
4 4
__str__ para que imprima el número racional 3
como 4/3, o bien 2
como 2
__add__ y __mul__ para que realice la suma y el producto entre dos números
racionales mediante los operadores + y *, respectivamente.
Los resultados deben mostrar:
a = Rational (6 ,4)
b = Rational (8 ,6)
print (a)
print (b)
3/2
4/3
print (a+b)
print (a*b)
17/6
2
Índice alfabético
funciones, 42 json, 36
mágicas, véase magic function math, 34
MySQLdb, 36
help, 36 os, 36
path.splitext, 51
identificador, 15 pbd, 36
palabras reservadas, 16 random, 36, 50
if, 39 randint, 50
import, 34, 44 re, 36
in, 31, 33, 39 shutil, 36
intérprete, 8 smtplib, 36
IPython, 10 sqlite3, 36
iterable, 32, 33, 39 statistics, 36
sys, 36, 46
Jupyter Notebook, 8, 12 path, 46
timeit, 36
len, 20, 26 xml, 36
list, 27, 38 xmlrpc, 36
listas, 19 zlib, 36
copia, 22
métodos None, 21
append, 20 not in, 33
copy, 49
extend, 21 operadores aritméticos
insert, 20 // división entera, 16
pop, 21 * multiplicación, 16
reverse, 20 % módulo, 16
sort, véase sorted ** potenciación, 16
operadores - resta, 16
* multiplicación, 21 + suma, 16
+ suma, 21 operadores aumentados, 17, 48
slicing, 22 operadores de comparación
!= distinto, 31
magic cell == igual, 31
% %writefile, 43 > mayor, 31
magic function, 11 >= mayor o igual, 31
%run, 11 < menor, 31
módulos <= menor o igual, 31
cmath, 35 operadores lógicos
copy, 49 and, 31
copy, 49 not, 31
deepcopy, 49 or, 31
csv, 36
datetime, 36 paquete, 51
fractions, 36 pass, 41
ftplib, 36 pow, 16
importación abreviada, 36 print, 9
importación masiva, 35 parámetros
importlib, 45 end, 38, 40
202 ÍNDICE ALFABÉTICO
sep, 16
range, 38
recolector de basura, 50
return, 42
scripts, 8
shallow copy, véase copia superficial
shebang, 9
sorted, 32
parámetros
key, 32
reverse, 32
str, véase cadenas de caracteres
tipos
bool, 31
complex, 16
dict, 26
float, 16
int, 16
list, 19
str, 24
tuple, 28
tuplas, 28
asignación múltiple, 29, 40
intercambio de variables, 29
type, 16
Unicode, 15, 25
while, 40