Introducción a Python
=====================
Python es un lenguaje de programación **interpretado**, como lo son Matlab o IDL, pero a diferencia de ellos, es de uso general y no específicamente orientado a ciencia o ingeniería. A diferencia de lenguajes interpretados como C/C++ o Fortran, que hay que compilar para crear un programa ejecutable, Python se ejecuta (interpreta) línea a línea, lo que lo hace ideal para en análisis interactivo de datos.
Además de incluir una extensa librería propia, existen módulos de terceros para prácticamente cualquier cosa. Python incluye una terminal estándar sobre la que se pueden ejecutar comandos, pero existen otras alternativas, como **ipython**, mucho más completas. Además Python se puede ejecutar como un programa ejecutable desde la línea comandos.
.. note::
**Python 2 vs Python 3**
Desde 2008 coexisten dos ramas de Python, Python 2 y Python 3. Aunque son muy
similares, existen algunas diferencias entre ellas que hacen que no son completamente
compatibles. La versión 2.7 será la última de Python 2, que llegará al `final de
de vida `__ (EOL) en enero de 2020,
no habiendo más actualizaciones ni corrección de errores. Por este motivo debe
usarse Python 3.
Si embargo, aun hay algunos programas y sistemas que usan Python 2 y es aún la versión
instalada por defecto en Linux y MacOS, por lo que conviene conocer las diferencias
entre ambos.
Empezaremos con la terminal estándar de Python escribiendo python en la terminal:
.. code-block:: python
japp@vega:~$ python3
Python 3.6.8 (default, Oct 7 2019, 12:59:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Esto es Python 3")
Esto es Python 3
Los tres símbolos de mayor (``>>>``) es el *prompt* que espera los comandos. Con la consola podemos hacer las operaciones matemáticas básicas directamente:
.. code-block:: python
>>> 23*119
2737
>>> 34 - 57 * (13 + 3)**2
>>> -14558
Sin embargo, ahora ya no estamos en la terminal de comandos del sistema, por lo no podemos interactuar con él directamente y los comandos del SO no funcionan:
.. code:: ipython3
>>> pwd
Traceback (most recent call last):
File "", line 1, in
NameError: name 'pwd' is not defined
Desde luego existen formas de llamar a commandos y programas del sistema como ya veremos, pero esto nos muestra algunas limitaciones de la consolar estándar. Por este y otros motivos nos conviene usar una consola de comandos avanzada como es ``IPython``. Si tenemos una instalación estándar de Python e IPython, podemos usar lanzar ``IPython`` desde la línea de comandos con ``ipython``; si usamos Anaconda podemos usar la **Jupyter Qtconsole**, que es una versión gráfica de ``IPython``.
.. code:: ipython3
japp@vega:~$ ipython3
Python 3.6.8 (default, Oct 7 2019, 12:59:55)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: ls
Documentos/ imagen.png texto.txt
In [2]: pwd
/home/japp
Como vemos, al menos los comandos básicos del sistema funcionan directamente. Lo más destacado de ``IPython`` son los `comandos mágicos `__ , que son funcionalidades adicionales muy útiles; todas empiezan con "**%**" y podemos ver una referencia rápida de estos con ``%quickref`` y una referencia general con ``%magic``. Pronto usarmos más comandos mágicos, por ahora empezaremos con ``%logstart`` para guardar un registro de nuestra sesión de trabajo.
.. code-block:: ipython3
In [4]: %logstart -o introduccion_a_python-20191125.py
Activating auto-logging. Current session state plus future input saved.
Filename : introduccion_a_python-20191125.py
Mode : backup
Output logging : True
Raw input log : False
Timestamping : False
State : active
In [5]:
donde el parámetro opcional ``-o`` guarda también la salida (respuesta) de Python en el fichero ``introduccion_a_python-16May2017.py``. De esta manera tendremos en un fichero todo lo que hicimos durante la sesión. Con los comandos ``%logoff`` y ``%logon`` podemos, respectivamente, detener temporalmente y continuar el registro de sesión que hayamos iniciado previamente con ``%logstart``. El nombre del fichero es libre y también puede tener cualquier extensión. En este ejemplo usamos ``.py`` porque en el fondo contiene código Python, pero en realidad no es exactamente un ejecutable porque contiene todos los errores que hayamos podido cometer en el proceso.
.. note::
**Comandos mágicos de IPython**
Algunos comandos mágicos más de IPython
%history: Muestra los últimos comandos usados.
%who y %whos: Lista simple y detallada de las variables declaradas en la sesión.
%edit : Abre un fichero con editor y después lo ejecuta.
%paste: Pega código en la terminal sin problemas de espacios y sangrado.
%timeit: calcula el tiempo ejecución de una línea de código.
Como casi todos los lenguajes de programación, Python distingue entre enteros (``int``), coma flotante (``float``) y cadenas de texto (``str``) aunque posee otros muchos tipos de datos. Como es habitual también en muchos lenguages, en **Python 2** las operaciones entre enteros siempre devuelven un entero, por ejemplo:
.. code-block:: python
# Usando Python 2
113/27
4
113/27.0
4.1851851851851851
Esto ocurre porque el tipo de dato de salida es siempre el de mayor precisión entre los valores de entrada; si solo se usan enteros, el **tipo de dato** devuelto será un entero aunque su valor numérico no lo sea. Esta situación cambia en **Python 3**, que cambia a ``float`` en divisiones exactas, pero hay que tenerlo muy en cuenta con Python 2.
.. code-block:: python
# Usando Python 3
113/27
4.185185185185185
# Asi se reproduce el funcionamiento de Python 2
113//27
4
La variables pueden llamarse usando cualquier cadena alfanumérica, pero sin caracteres especiales como espacios, o ``&, $, *,`` etc., siempre que no empiece con un número. Además distingue entre mayúsculas y minúsculas y no es necesario declararlas previamente.
.. code-block:: ipython
In [10]: frase = "Esta es una linea de texto"
In [11]: num = 22
In [12]: num*2
Out[12]: 44
In [13]: frase*2
Esta es una linea de textoEsta es una linea de texto
Aquí, ``frase`` es una variable tipo ``str`` (string) mientras que ``num`` es otra variable numérica entera ``int``. Nótese que mientras se multiplicó ``num`` por 2 como era de esperar, la cadena de texto ``frase`` fue duplicada al multiplicarla por 2. En este caso hemos operado con dos tipos de datos distintos, un ``string`` y un ``int``, algo que muchos lenguajes de programación produce un error por no tener sentido, sin embargo, Python interpreta el producto de un ``string`` y un ``int`` como la unión o concatenación de la misma cadena de texto varias veces, y es por eso que vemos frase duplicada. Los tipos de datos numéricos de Python son ``int``, ``float`` y ``complex``. En Python 2 existe el entero ``long``, pero en Python 3 solo hay enteros tipos ``int`` que básicamente se comporta como ``long`` de Python 2.
A lo largo del trabajo podemos acabar definiendo muchas variables de distinto tipo. Con el comando ``type()`` podemos saber el tipo de dato que se asigna a una variable si en cualquier momento no recordamos como la definimos:
.. code-block:: ipython
In [15]: type(frase)
Out[15]: str
In [16]: type(num)
Out[16]: int
In [17]: complejo = 1.2 + 5.0j # tambien: complex(1.2, 5.0)
In [18]: type(complejo)
Out[18]: complex
Algunos tipos de datos pueden convertirse de unos a otros, empleando ``str()`` para convertir a cadena de texto, ``int()`` a entero y ``float()`` a coma flotante:
.. code-block:: ipython
In [20]: float(3)
3.0
In [21]: int(3.1416) # Parte entera del float
3
In [22]: str(34)
'34'
Para el caso de los ``float``, se pueden redondear con ``round()``, que redondea al entero más próximo. Las funciones ``ceil()`` y ``floor()`` del módulo :mod:`math` redondean hacia arriba y hacia abajo respectivamente, como veremos mas adelante:
.. code-block:: ipython
In [30]: print(round(4.4)) , (round(4.5))
Out[30]: 4.0 5.0
Además de los operadores aritméticos conocidos ``**`` (exponenciación, ``^`` es ``XOR`` en operaciones bit a bit), ``*, /, % (módulo), +\`` y ``-``, tenemos los operadores lógicos:
=================================== ============
Operación Símbolo
=================================== ============
Igual a (comparación) ==
Distinto de (comparación) != o <>
Mayor que, Menor que >, <
Mayor o igual, Menor o igual >=, =<
y, o and, or
cierto, falso True, False
=================================== ============
Como resultado de una operación lógica, obtenemos como respuesta un elemento booleano ``True`` o ``False``, según se verifique o no la operación. Estos elementos lógicos los podemos usar a su vez para otras operaciones. Veamos algunos ejemplos:
.. code:: ipython3
In [40]: resultado = 8 > 5
In [41]: print(resultado)
True
In [42]: resultado = (4 > 8) or (3 > 2)
In [43]: print(resultado)
True
In [44]: resultado = True and False
In [45]: print(resultado)
False
In [46]: resultado = (4 > 8) and (3 > 2)
In [47]: print(resultado)
False
Usando los operadores lógicos, podemos consultar si una variable es de un tipo en concreto:
.. code:: ipython3
In [50]: numero = 10.0
In [51]: type(numero) == int
Out [51]: False
In [52]: type(numero) == float
Out [52]: True
En este caso ``int`` y ``float`` no son cadenas de texto, sino un indicador del tipo de dato. Esto se puede usar para cualquier tipo de dato más complejos, no únicamente números o cadenas, los cuales veremos más adelante.
Cadenas de texto
----------------
Las cadenas de texto, como hemos visto, no son mas que texto formado por letras y números de cualquier longitud y son fácilmente manipulables. Para poder hacerlo, cada carácter de una cadena de texto tiene asociado un índice que indica su posición en la cadena, siendo 0 el de la izquierda del todo (primero), 1 el siguiente hacia la derecha y así sucesivamente hasta el último a la derecha. Aquí hay algunos ejemplos:
.. code:: ipython3
In [52]: # Variable "frase" que contiene una cadena de texto
In [53]: frase = "hombros de gigantes"
In [54]: print(frase[0]) # Primera letra de la cadena
h
In [55]: print(frase[11]) # Decimosegunda letra, con índice 11
g
In [57]: print(frase[11:18]) # Seccion de la cadena
gigante
In [58]: print(frase[11:]) # Desde el indice 11 hasta el final
gigantes
In [58]: print(frase[:10]) # Desde el principio al caracter de
hombros de # indice 10, sin incluirlo
.. figure:: img/python-indexing.png
:alt: indexado de cadenas
Indexado de cadenas de texto usando índices positivos empezando desde la izquierda con 0 y negativos, empezando desde la derecha con -1.
El comando ``len()`` nos da el número de caracteres (longitud) de la cadena de texto, incluyendo espacios en blanco y caracteres especiales:
.. code:: ipython3
In [60]: len(frase) # 75 es el número de caracteres de la cadena
Out [60]: 19
In [61]: print(frase[len(frase)-1]) # El último carácter, contando desde la izquierda
s
In [62]: print( frase[-1] == frase[len(frase)-1] ) # Compruebo si son iguales
True
Como vemos arriba, también se pueden referir con índices contando desde la derecha, usando índices negativos, siendo -1 el primero por la derecha:
.. code:: ipython3
In [63]: print(frase[-1]) # El último carácter, contando desde la derecha
s
Si hacemos ``dir()`` de la variable frase, veremos los métodos (funciones) que se pueden aplicar ``frase`` por ser un ``string``. Los métodos son simplemente funciones específicas para una tipo (objeto) de dato:
.. code:: ipython3
In [65]: type(frase)
Out [65]: str
In [66]: dir(frase)
Out [66]: ['__add__',
'__class__',
'__contains__',
'__delattr__',
'__doc__',
.
.
'upper',
'zfill']
los primeros, que empiezan y terminan con "\_\_" son *variables internas* del módulo, un tipo de métodos y propiedades especiales; el resto son los métodos normales. Una manera más práctica de verlos, gracias a IPython es escribir un punto después de ``frase`` (u otro tipo de variable) y luego presionar el tabulador:
.. code:: ipython3
In [67]: frase.
frase.capitalize frase.isalnum frase.lstrip frase.splitlines
frase.center frase.isalpha frase.partition frase.startswith
frase.count frase.isdigit frase.replace frase.strip
frase.decode frase.islower frase.rfind frase.swapcase
frase.encode frase.isspace frase.rindex frase.title
frase.endswith frase.istitle frase.rjust frase.translate
frase.expandtabs frase.isupper frase.rpartition frase.upper
frase.find frase.join frase.rsplit frase.zfill
frase.format frase.ljust frase.rstrip
frase.index frase.lower frase.split
Es así además como se aplican los método en Python, con la sintaxis ``variable.metodo``. Aquí hay algunos ejemplos:
.. code:: ipython3
In [70]: frase_mayusculas = frase.upper() # Cambia a mayusculas y lo guardo en
In [71]: print(frase_mayusculas) # la variable frase_mayusculas
HOMBROS DE GIGANTES
In [72]: frase_titulo = frase.title() # Cambia la primera letra de cada palabra a mayuscula
# y lo guarda en la variable frase_minusculas
In [73]: print(frase_titulo)
Hombros De Gigantes
In [74]: # Reemplaza una cadena de texto por otra
In [75]: frase.replace("gigantes", "enanos")
'hombros de enanos'
Estos comandos devuelven una nueva cadena de texto cambiada, que vemos por pantalla, sin modificar la variable original, aunque no siempre es así. Podemos comprobar que la frase no se ha alterado de forma permanente haciendo ``print(frase)``.
Para cambiar la variable ``frase`` deberemos volver a definir la cadena de texto:
.. code:: ipython3
In [76]: # Reemplaza definitivamente una cadena de texto por otra
In [77]: frase = frase.replace("gigantes", "enanos")
In [78]: print(frase)
'hombros de enanos'
Formato de texto
----------------
A menudo queremos imprimir texto con valores numéricos de algún tipo, pero esto no es posible porque *string* y números son tipos de datos distintos. La forma más básica de mezcar cadenas y números es convirtiendo los números a cadenas y concatenándolas:
.. code:: ipython3
In [80]: a, b = 10, 10**2 # Definimos dos numeros, a=10 y b=10**2
In [81]: print(str(a) + " elevado al cuadrado es " + str(b))
10 elevado al cuadrado es 100
En Python 3, la impresión es algo más flexible, poniendo un número indefinido de parámetros de distinto tipo separados por comas y ``print()`` los une con un espacio o con la cadena que le indiquemos en el parámetro ``sep``, que es opcional:
In [80]: print(a, "elevado al cuadrado es", b)
10 elevado al cuadrado es 100
In [81]: print(a, " elevado al cuadrado es", b, sep=";")
10;elevado al cuadrado es;100
Sin embargo, la manera más práctica y correcta de hacer esto es imprimiendo los números con el formato que queramos con la sintaxis de formato que Python hereda de C:
.. code:: ipython3
In [82]: # Calculamos el logaritmo base 10 de 2 e imprimimos
# el resultado con 50 decimales
In [83]: print("%.50f" % log10(2.0**100))
30.10299956639811824743446777574717998504638671875000
In [84]: # Otro ejemplo usando texto, enteros y decimales
In [85]: print("El %s de %d es %f." % ('cubo', 10, 10.**3) )
El cubo de 10 es 1000.000000.
Aquí se reemplaza cada símbolo ``%s`` (para cadenas de texto), ``%d`` (para enteros) o ``%f`` (para floats) sucesivamente con los valores después de ``%`` que están entre paréntesis. En caso de los *floats* se puede utilizar el formato ``%10.5f``, que significa imprimir 10 caracteres en total, incluído el punto, usando 5 decimales. El formato científico se emplea utilizando ``%e``, por ejemplo:
.. code:: ipython3
In [86]: print("%.5e" % 0.0003567)
3.56700e-04
In [87]: # Otra forma de hacerlo (sin imprimir)
In [88]: resultado = format(0.0003567, ".5e")
In [89]: resultado
Out[89]: '3.56700e-04' # es un string
Los formatos son muy útiles a la hora de expresar el resultado de un cálculo con los dígitos significativos solamente o con la indicación del error en el resultado. Así por ejemplo, si el resultado de un cálculo o de una medida es 3.1416 :math:`\pm` 0.0001 podemos expresarlo como:
.. code:: ipython3
In [90]: # resultado de un cálculo obtenido con las cifras
In [91]: # decimales que proporciona el ordenador
In [92]: resultado = 3.1415785439847501
In [93]: # este es su error con igual número de cifras decimales
In [94]: error = 0.0001345610900435
In [95]: # así expresamos de forma correcta el resultado
In [96]: print("El resultado del experimento es %.4f +/- %.4f" % (resultado, error) )
El resultado del experimento es 3.1416 +/- 0.0001
En la línea 96 imprimimos dos valores, que **deben darse entre paréntesis y separados por comas** si es más de uno.
Además de esta sitaxis clásica, Python tiene un cuasi-lenguaje propio similar para dar formato a las cadenas usando el método ``format()``. Este sistema propio de Python es más flexible y potente:
.. code:: ipython3
In [96]: print("{:.2f}".format(3.1415926))
3.14
============ ========= ============ ===============================================
Número Formato Salida Descripción
============ ========= ============ ===============================================
3.1415926 {:.2f} 3.142 decimal places
3.1415926 {:+.2f} 3.142 decimal places with sign
-1 {:+.2f} -12 decimal places with sign
2.71828 {:.0f} 3 No decimal places
5 {:0>2d} 5 Pad number with zeros (left padding, width 2)
5 {:x<4d} 5xxx Pad number with x's (right padding, width 4)
10 {:x<4d} 10xx Pad number with x's (right padding, width 4)
1000000 {:,} 1000000 Number format with comma separator
0.25 {:.2%} 25.00% Format percentage
1000000000 {:.2e} 1000000000 Exponent notation
13 {:10d} 13 Right aligned (default, width 10)
13 {:<10d} 13 Left aligned (width 10)
13 {:^10d} 13 Center aligned (width 10)
============ ========= ============ ===============================================
Aquí hay algunos ejemplos de cómo se usan::
frase2 = "A hombros de {}".format("gigantes")
frase3 = " {0} es mejor que {1} ".format("Python", "IDL")
resultado = "El {operacion} de {numero} es {resultado}".format(operacion="cubo", numero=7, resultado=7**3)
# Devuelve:
A hombros de gigantes
Python es mejor que IDL
El cubo de 7 es 343
Esta especie de lenguaje tiene muchas más opciones, así que lo mejor es consultar la `documentación oficial `__ o `alguna guía más detallada `__.
Estructuras de datos
--------------------
Además de los tipos de datos univaluados, Python posee otros tipos de datos más complejos, las **estructuras de datos**. que permiten organizar y manipular varios elementos. El más común es la lista (``list``), que se crea con corchetes con los elementos separados por comas::
estrellas = ["Alhena", "Mizar", "Cor Caroli", "Nunki", "Sadr"]
datos = ["Beta pictoris", 1.6, [1, 2, -3]]
Con el método ``split()`` podemos separar un ``string`` en una lista de elementos, separando por defecto por espacios; se puede añadir un segundo parámetro opcional para separar por otro(s) caracter.
.. code:: ipython3
In [100]: palabras = frase.split()
In [101]: print(palabras)
['hombros', 'de', 'gigantes']
La listas se indexan prácticamente igual que las cadenas, donde cada elemento tiene un índice:
.. code:: ipython3
In [102]: estrellas[-1] # El último elemento
Out[102]: 'Sadr'
In [103]: estrellas[1:3] # Otra lista, del segundo (índice 1) al tercero (indice 2)
Out[103]: ['Mizar', 'Cor Caroli']
En el último ejemplo es importante darse cuenta que ``estrellas[1:3]`` **no incluye el último elemento**, de manera devuelve dos elementos; esto es fácil de prever restando el último del primero: ``3-1 = 2`` elementos.
Con el método ``range()`` tenemos un iterador de números enteros. Un iterador es un tipo de dato que posee métodos para iterar los elementos que contiene, que son similares a los de una lista. De hecho, en Python 2 la función ``range()`` no devuelve un iterador ``range``, si no directamente una lista de enteros. En Python 3 podemos convertir el iterador ``range`` con ``list(range)``.
Admite hasta tres parámetros (ver ``help(range)`` o ``range?``), aunque sólo uno es obligatorio, que es el número de elementos, que irá de ``0`` a ese número, sin incluirlo:
.. code:: ipython3
In [104]: # Iterador (range) de enteros de 0 a 4
In [105]: range(5)
Out[105]: range(0, 5)
In [106]: list(range(5)) # convierto el iterador a lista (no necesario en Python 2)
Out[106]: [0, 1, 2, 3, 4]
In [107]: # Lista de enteros de 10 a 15
In [108]: list(range(10, 16))
Out[108]: [10, 11, 12, 13, 14, 15]
In [108]: # Lista de enteros de 10 a 20 (el 21 no se incluye), de dos en dos
In [109]: list(range(10, 21, 2))
Out[109]: [10, 12, 14, 16, 18, 20]
Las listas tienen métodos similares a las cadenas de texto y también varios métodos para manipularlas y transformalas:
.. code:: ipython3
In [110]: len(estrellas)
Out[110]: 5
In [111]: estrellas.
estrellas.append estrellas.index estrellas.remove
estrellas.count estrellas.insert estrellas.reverse
estrellas.extend estrellas.pop estrellas.sort
In [112]: estrellas.append("Ras Algethi") # Añado Ras Algethi al final de la lista
In [113]: print(estrellas)
['Alhena', 'Mizar', 'Cor Caroli', 'Nunki', 'Sadr', 'Ras Algethi']
In [114]: estrellas.insert(3, "Hamal") # Añado Hamal en el cuarto lugar (índice 3)
In [115]: print(estrellas)
['Alhena', 'Mizar', 'Cor Caroli', 'Hamal', 'Nunki', 'Sadr', 'Ras Algethi']
In [116]: estrellas.pop() # Estraigo el último elemento (lo devuelve)
Out[116]: 'Ras Algethi'
In [117]: estrellas.pop(3) # Estrae el elemento de índice 3
Out[117]: 'Hamal'
In [118]: estrellas.remove("Nunki") # Elimina la primera ocurrencia de Hamal
In [119]: estrellas.sort() # Ordena la lista original alfabéticamente
In [120]: estrellas
Out[120]: ['Alhena', 'Cor Caroli', 'Mizar', 'Sadr']
Aunque podemos manipular mucho las listas, existen funciones como ``map()`` o ``filter()`` que nos permite iterar con sus elementos; además el módulo `itertools `_ ofrece métodos adicionales para trabajar con listas y otros iterables. Veremos algunos ejemplos más adelante.
Otro tipo de dato estructurado son las **tuplas**, que básicamente son listas inalterables. Tienen propiedades de indexado similares, pero no poseen métodos para modificarlos porque no se pueden cambiar. Se declaran entre paréntesis en lugar de corchetes y se suelen usar en parámetros de funciones y datos que no se suelen modificar. Cuando definimos una serie de datos de cualquier tipo separados por comas, se considera una tupla, aunque no estén entre paréntesis.
.. code:: ipython3
In [122]: c = (1, 3) # Defino una tupla
In [123]: print(c)
(1, 3)
In [124] parametros = 4, 5, 2, -1, 0 # Creamos otra tupla al indicar varios datos separados
# por comas, aunque no estén entre paréntesis
In [125] type(parametros)
Los **diccionarios** son estructuras que en lugar de índices numéricos tienen índices de cadenas declarados por nosotros, lo que es muy útil para describir propiedades.
.. code:: ipython3
In [128] star = {'name': 'Hamal', 'mag': 2.0, 'SpT': 'K2III', 'dist': 66}
In [129] type(star)
Out[129]
En este caso hemos creado una clave "name" con valor "Hamal", otra clave "mag" con valor 2.0, etc. Al crear los datos con esta estructura, podemos acceder a los valores de las claves fácilmente, así como definir nuevas parejas clave-valor:
.. code:: ipython3
In [130]: print(star['name'])
Hamal
In [131]: star['parallax'] = 49.56 # Añadimos un nuevo dato
Hay que notar que ya no podemos acceder a los datos por su índice, ya que los diccionarios se indexan exclusivamente por la clave y obtenendremos un error si lo hacemos. Además, el orden de los elementos no será necesariamente el con el que lo hemos creado, porque éste se pierde al carecer de índices numéricos:
.. code:: ipython3
In [134]: print(star[0])
Traceback (most recent call last):
File "", line 1, in
KeyError: 0
In [135]: print(star) # demonios, no estan en el orden original!
{'dist': 66, 'SpT': 'K2III', 'name': 'Hamal', 'mag': 2.0}
Podemos conocer todas las claves y los valores de un diccionario usando los métodos ``keys()`` y ``values()`` respectivamente:
.. code:: ipython3
In [135]: star.keys()
Out[135]:['dist', 'SpT', 'name', 'mag']
In [136]: star.values()
Out[136]: [66, 'K2III', 'Hamal', 2.0]
Finalmente, también existen los ``set`` que se podrían llamar *conjuntos*, que son colecciones sin ordenar de elementos no duplicados. Son útiles probar pertenencias a listas eliminar entradas duplicadas, además de tener operaciones matemáticas como unión, intersección, etc.
.. code:: ipython3
In [140]: estrellas1 = set( ("Alhena", "Mizar", "Cor Caroli") )
In [141]: estrellas2 = set( ("Mizar", "Cor Caroli", "Nunki", "Sadr") )
In [141]: "Alhena" in estrellas1 # Esto también funciona con listas y tuplas
Out[141]: True
In [142]: "Alhena" in estrellas2
Out[142]: False
In [143]: # Union de dos conjuntos
In [144]: estrellas1 | estrellas2
Out[144]: {'Alhena', 'Cor Caroli', 'Mizar', 'Nunki', 'Sadr'}
In [145]: # Intersección, elementos comunes
In [146]: estrellas1 & estrellas2
Out[147]: {'Cor Caroli', 'Mizar'}
In [148]: # Diferencia, los que están en estrellas1 pero no en estrellas2
In [149]: estrellas1 - estrellas2
Out[149]: {'Alhena'}
In [150]: # Diferencia simétrica, que son únicos en cada conjunto
In [150]: estrellas1 ^ estrellas2
Out[151]: {'Alhena', 'Nunki', 'Sadr'}
Módulos de Python
-----------------
Al iniciar la terminal de Python tenemos disponibles de inmediato todos los tipos de datos y funciones que hemos visto hasta ahora, pero podemos tener funcionalidades adicionales importado módulos de Python, ya sean de la librería estándar o externo. Por ejemplo, si queremos usar funciones matemáticas básicas debemos importar el módulo :mod:`math` de la librería estándar de Python para poder usarlas:
.. code:: ipython3
In [160]: import math
In [161]: print(math.sin(0.5*math.pi))
1.0
In [162]: dir(math)
['__doc__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs',
'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp',
'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin',
'sinh', 'sqrt', 'tan', 'tanh', 'trunc']
In [164]: a, b = 4.4, 4.5
In [165]: print(math.ceil(a)), (math.ceil(b))
Out[165]: 5.0 5.0
In [166]: print(floor(a)), (math.floor(b))
Out[166]: 4.0 4.0
Arriba usamos ``dir()`` para ver el contenido del módulo, sus métodos y propiedades disponibles, que no son más que la librería matemática estándar de C. Además del listado, podemos obtener ayuda de un módulo o método (función) usando el comando ``help()``. Más adelante veremos cómo funcionan los módulos y paquetes en Python.
Programas ejecutables
---------------------
Hasta ahora hemos trabajado iterativamente con la terminal de Python, pero podemos crear programas ejecutables más complejos con cualquier editor de código. Prácticamente cualquier editor clásico como Emacs, Vim, Kate, etc. nos servirá, pero es muy recomendable usar el IDE Spyder, específicamente hecho para Python y con muchas ventajas.
Veamos un ejemplo sencillo de un programa ejecutable:
.. code-block:: python
#!/usr/bin/python3
#-*- coding: utf-8 -*-
# Mensaje de bienvenida
print("Programa de calculo del cubo de un numero.\n")
# Numero de entrada
x = 23.0
# Calculo el valor del cubo de x
y = x**3
# Imprimo el resultado
print("El cubo de {:.2f} es {:.2f}".format(x, y))
El programa se puede ejecutar ahora desde una consola desde el directorio en donde tenemos el archivo con el programa, escribiendo en ella:
.. code-block:: bash
$ python3 cubo.py
y la respuesta del programa será:
.. code-block:: bash
Programa de calculo del cubo de un numero.
El cubo de 23.00 es 12167.00
En la primera línea del programa hemos escrito ``#!/usr/bin/python3`` para indicar la ruta donde tenemos instalado python. La segunda línea, ``#-\ *- coding: utf-8 -*-`` hemos indicado el tipo de codificación UTF-8, para poder poner caracteres especiales como tildes y eñes.
Ya que la primera línea indica en qué directorio está el ejecutable de python, el programa también se puede ejecutar como un comando o programa del sistema, escribiendo únicamente:
.. code-block:: bash
$ ./cubo.py
Si quieres ejecutar el programa desde dentro de la consola estándar de python, puedes usar lo ``execfile()``::
>>> execfile('cubo.py')
de igual manera, si estamos usando IPython podemos usar el comando mágico ``%run``, que en fondo es una llamada a ``execfile()``:
.. code:: ipython3
%run cubo.py
La ventaja en este último caso con IPython es que una vez ejecutado, las variables y funciones definidas en el programa lo estarán ahora lo estarán en la sesión de IPython, lo que es muy útil para probar y corregir nuestro programa interactivamente.
.. code-block:: python
print(x)
23.0
es decir, vemos ahora tenemos la variable x está definida en nuestra sesión.
Podemos definir funciones reutilizables con el comando ``def()``, por ejemplo, para usarlo en nuestro programa anterior:
.. code-block:: python
# Definimos una función que calcula el cubo de un número cualquiera
def cubo(x):
y = x**3
return y
# Utilizamos la funcion para calcular el cubo de 4
resultado = cubo(4.0)
Ahora podemos llamar a la función ``cubo()`` cuando queramos.
**¡ALTO!:** Aquí ha pasado algo importante. Fíjense en la definición de ``cubo(x)`` ¿cómo sabe Python donde empieza y termina la función si no hay llaves que abren ni cierran o palabras clave del tipo "DO BEGIN" o "END" como en otros lenguajes? La clave está en el **sangrado** (identación), que es **obligatorio en Python** ya que es como se indican dónde empiezan y terminan los bloques de código. No hay número específico de espacios que haya que poner (pero lo recomendado es cuatro), lo importante es que las líneas **estén en el mismo bloque de sangrado**. En nuestro ejemplo sencillo, la función empieza después de ":" y termina donde el margen vuelve a ser el original.
También es fundamental **no mezclar espacios y tabuladores**, porque puede producir errores de sangrado (*indentation error*), por lo que se recomendable configurar el editor de manera que escriba siempre espacios al pulsar el tabulador.
En Python, la variables definidas dentro de una función **son locales**, lo que quiere decir que sólo están definidas dentro de la función y no están accesibles fuera. Podemos comprobar esto con una pequeña modificación del programa:
.. code:: ipython3
In [168]: def cubo(x):
...: potencia = 3
...: return x**potencia
...:
In [169]: cubo(3.5)
Out[169]: 42.875
In [170]: print(potencia)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 potencia
NameError: name 'potencia' is not defined
Como vemos, aunque función funciona correctamente, la variable ``potencia`` que definimos dentro no existe fuera de la función. Esto igualmente cierto para funciones definidas dentro de funciones. Cuando creamos variables y funciones fuera de una función, como hecho hasta ahora, son todas **globales**.
Además de sentencia de ``def()`` que crea funciones ordinarias, existen la **funciones anónimas** o **función lambda** que permiten definir funciones simples en una sola línea:
.. code-block:: python
cubo = lambda x: x**3
cubo(3)
El comando ``def()``, éste permite varios parámetros en entrada, que pueden ser opciones si se incluye un valor por defecto:
.. code-block:: python
def saludo(nombre, apellido=""):
print "Hola {} {}".format(nombre, apellido)
saludo('Carmen')
Hola Carmen
Los valores de los parámetros se asignan en el orden en el que están definidos en la función, pero si se usan con su nombre, pueden ponerse en cualquier orden. Por ejemplo:
.. code-block:: python
saludo(apellido="Prieto", nombre="Gloria")
Hola Gloria Prieto
Es posible tener un número indeterminado de parámetros, dados en forma de tupla o diccionario, en cuyo caso en la definición los parámetros deben empezar con **\*** o **\*\*** respectivamente:
.. code-block:: python
def datos_estrella(*args, **kwargs):
print("Nombre", args)
print("Datos", kwargs)
datos_estrella('Altair', mag=2.2, SpT="A7V")
Nombre ('Altair',)
Datos {'SpT': 'A7V', 'mag': 2.2}
En este ejemplo vemos que la función ``datos_estrella()`` admite un número indefinido de parámetros sin nombre, que se agruparán en una lista llamada ``args`` y también un número indefinido de parámetros con nombre (con parejas clave=valor), que se agruparán en una lista llamada ``kwargs`` en nuestro ejemplo.
Uso de funciones con iterables
------------------------------
Podemos usar funciones con tipos de datos basicos como ``strings`` o ``float``, pero no podemos aplicarlos directamente a listas o tuplas. Por ejemplo, no podemos hacer ``cubo(range(5))`` esperando obtener una lista de resultado. Si embargo, podemos usar algunos métodos como ``map()`` o ``filter()`` para operar recursivamente con iterables, como mencionamos antes. Veamos este ejemplo:
.. code-block:: python
n = range(10)
def raiz(x):
return x**0.5
# Aplica una función a un iterable
resultado = map(raiz, n) # Devuelve una lista (python 2) o iterable (python 3)
# Devuelve una lista (python 2) o iterable (python 3) para los elementos
# del iterable que son True
mayores5 = filter((lambda x: x >=5), n)
Entrada de datos en programas
-----------------------------
Los programas ejecutables que creamos necesitan a menudo datos de entrada que pueden ser distintos cada vez que se usan, e incluirlos directamente como variables dentro del código puede no ser muy eficiente ya que tendríamos que abrir y modificar el fichero cada vez que cambiamos los parámetros.
Podemos usar la función ``input()`` (``raw_input()`` en Python 2) para pedir entradas por teclado al usuario. Modificamos nuestro pequeño programa para usarlo:
.. code-block:: python
# Numero de entrada
x = input('Dame un numero: ')
x = float(x)
resultado = cubo(x)
print("El cubo de {} es {}".format(x, resultado))
Sin embargo, ``input()`` devuelve siempre un *string* aunque se de un valor numérico, por eso hay que transformarlo antes a entero o float para operar con el. Una alternativa a convertir strings en valores numéricos es emplear la función ``eval()``, que evalúa un string como si fuese código y devuelve el resultado, incluyendo una lista si el string contiene comas:
.. code-block:: python
x = 3.1416
eval("x**3") # Hace la operación y devuelve un float
31.006494199296
eval("10, 20, 30") # Si es un string con comas devuelve una tupla
(10, 20, 30)
Una manera alternativa de dar parámetros de entrada a un programa es usar argumentos justo después de la llamada del programa, para lo que necesitamos la función ``argv`` del módulo ``sys``. Se usa de la siguiente forma:
.. code-block:: python
from sys import argv
# Los parámetros pasados están en una lista de strings en argv, en la que
# el primer elemento es el nombre del programa y el segundo el primer parámetro
x = argv[1]
x = float(x)
resultado = cubo(x)
Ahora podemos llamar al programa poniendo los parámetros después:
.. code-block:: bash
# Para calcular el cubo de 6.5
./cubo.py 6.5
Se puede añadir un número indefinido de parámetros, pero como vemos debe hacerse sin nombres.
Una opción más sofisticada, pero también algo más compleja, es emplear el módulo `argparse `__, que permite definir parámetros de entrada complejos con varias opciones. Consideremos un programa que puede calcular la raíz o el cubo de un número, según decida el usuario. Podemos hacerlo de la siguiente manera:
.. code-block:: python
import argparse
from math import sqrt
parser = argparse.ArgumentParser()
# Dos argumentos obligatorios posibles, el numero a calcular
# y la operacion a realizar
parser.add_argument("-n", "--numero", help="Numero a calcular",
type=float, required=True)
parser.add_argument("-o", "--oper", help="Operacion a realizar: cubo o raiz",
type=str, default='cubo')
args = parser.parse_args()
# Funciones de las operaciones que puede hacer el programa
def cubo(x):
y = x**3
return y
def raiz(x):
return sqrt(x)
# Hacemos una operación u otra según la opcion elegida por el usuario
if args.oper == 'cubo':
print("El cubo de {0} es {1}".format(args.numero, cubo(args.numero)))
elif args.oper == 'raiz':
print("La raiz de {0} es {1}".format(args.numero, raiz(args.numero)))
else:
print("Error, operacion desconocida")
Ahora podemos añadir argumentos durante la ejecución del programa y además genera automáticamente un mensaje de ayuda con los argumentos disponibles usando ``--help``.
.. code-block:: console
./cubo-argparse.py --help
usage: cubo-argparse.py [-h] [-n NUMERO] [-o OPER]
optional arguments:
-h, --help show this help message and exit
-n NUMERO, --numero NUMERO
Numero a calcular
-o OPER, --oper OPER Operacion a realizar: cubo o raiz
./cubo-argparse.py -n 10 -o cubo
El cubo de 10.0 es 1000.0
./cubo-argparse.py -n 10 --oper raiz
La raiz de 10.0 es 3.16227766017
Control de flujo
----------------
Python tiene los elementos de control de flujo más comunes: **if-then-else**, **for** y **while**, con la peculiaridad que se usan los bloques de sangrado para delimitarlos. El bucle **for** se suele usar para recorrer iterables (listas, tuplas, etc.), deteniéndose cuando se agota la lista:
.. code-block:: python
for nombre in estrellas:
print(nombre)
Alhena
Cor Caroli
Mizar
Sadr
Alternativamente, podemos recorrer una lista de índices y llamar a cada elemento con su índice:
.. code-block:: python
for i in range( len(estrellas) ):
print("{0}) {1}. ".format(i+1, estrellas[i]))
1) Alhena.
2) Cor Caroli.
3) Mizar.
4) Sadr.
Si estamos trabajando con un diccionario, no podemos hacer esto directamente porque no tienen índices numéricos, pero podemos emplear el método ``items()`` (``iteritems()`` en Python 2) para iterar parejas ``clave:valor``:
.. code-block:: python
for clave, valor in star.items():
print("{0}: {1}".format(clave, valor))
dist: 66
SpT: K2III
name: Hamal
mag: 2.0
En este ejemplo tenemos que usar dos variables mudas (parejas ``clave`` y ``valor``) en lugar de una para capturar cada clave y valor del diccionario.
Los bucles **for** nos permiten crear nuevas lístas dinámicamente:
.. code-block:: python
# Importamos la función log10 del módulo math
from math import log10
b = [2.3, 4.6, 7.5, 10.]
c = [log10(x) for x in b]
print(c)
# Resultado:
# [0.36172783601759284, 0.66275783168157409, 0.87506126339170009, 1.0]
**¡Momento!** ¿Qué pasaría si en la lista de números ``b`` está el ``0``?
.. code-block:: python
In [78]: b = [0, 2.3, 4.6, 7.5, 10.]
In [79]: c = [log10(x) for x in b]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in ()
----> 1 c = [log10(x) for x in b]
ValueError: math domain error
Desde luego que da un error ¿qué hacemos? Aquí está la flexibilidad de Python:
.. code-block:: python
c = [log10(x) for x in b if x > 0]
El bucle **while** repite una serie de órdenes mientras una condición sea cierta (vale ``True``):
.. code-block:: python
cuentas = 0
while cuentas < 6:
print(cuentas)
cuentas = cuentas + 1
De manera similar poder hacer que ``while`` funcione mientras **no se cumpla una condición** usando ``while not``:
.. code-block:: python
x = 0
while not x == 5:
x = x + 1
print("x = %d" % x)
""" Resultado que obtenemos del programa:
x = 1
x = 2
x = 3
x = 4
x = 5
"""
En el ejemplo anterior hemos hecho una comparación de igualdad ``x == 5`` usando enteros, pero hay que tener cuidado cuando se comparan mediante una igualdad exacta números decimales o de coma flotante floats entre sí. Debido a la precisión finita de los ordenadores, es posible que una determinada igualdad nunca se cumpla exactamente y por lo tanto la ejecución del bucle nunca se detendrá. Podemos comprobar esto con un ejemplo en el que imprimimos los números que van de 0.0 a 1.0 a intervalos de 0.1,:
.. code-block:: python
x = 0.0
# Mientras x no sea exactamente 1.0, suma 0.1 a la variable *x*
while not x == 1.0:
x = x + 0.1
print("x = {:19.17f}".format(x))
""" Resultado que obtenemos:
x = 0.10000000000000001
x = 0.20000000000000001
x = 0.30000000000000004
x = 0.40000000000000002
x = 0.50000000000000000
x = 0.59999999999999998
x = 0.69999999999999996
x = 0.79999999999999993
x = 0.89999999999999991
x = 0.99999999999999989 <-- El bucle while debió detenerse aquí, pero no lo hizo
x = 1.09999999999999987
x = 1.19999999999999996
x = 1.30000000000000004
.
.
. <-- Presionar Ctrl+C para detener el programa
"""
y así el bucle no se para nunca. El código anterior produce un **bucle infinito** porque la condición ``x == 1.0`` nunca se da exactamente (el valor más cercano es 0.99999999999999989 pero no es 1.0). La conclusión que podemos extraer de aquí es que es preferible no comparar nunca variables o números de tipo float exactamente.
Una opción para resolver el problema anterior es usar rangos de precisión en el que definimos lo que para nosotros es **suficientemente cercano** al valor deseado. Así, con el ejemplo anterior podríamos hacer:
.. code-block:: python
x = 0.0
# Condición de que nuestro numero se acerque a 1.0
# al menos en 1e-8
while abs(x - 1.0) > 1e-8:
x = x + 0.1
print("x = %19.17f" % x)
""" Resultado que obtenemos:
x = 0.10000000000000001
x = 0.20000000000000001
x = 0.30000000000000004
x = 0.40000000000000002
x = 0.50000000000000000
x = 0.59999999999999998
x = 0.69999999999999996
x = 0.79999999999999993
x = 0.89999999999999991
x = 0.99999999999999989
"""
Finalmente, el **if-then-else** funciona de forma habitual, siendo la sentencia ``if`` la única obligatoria, pudiendo poner indefinidos **if alternativos** con ``elif`` y finalizar opcionalmente con ``else``. Veamos un ejemplo simple:
.. code-block:: python
# un numero a valorar
c = 12
if c > 0: # comprueba si es positivo
print("La variable c es positiva")
elif c < 0: # si no lo es, comprueba si es negativo
print("La variable c es negativa")
else: # Si nada de lo anterior se cumple, haz lo siguiente
print("La variable c vale 0")
Si primer bloque con ``if`` se cumple (``c > 0`` es ``True``), se ejecuta ese bloque en ``if`` y únicamente ese bloque. Si no se cumple (``c > 0`` es ``False``), se comprueban sucesivamente el resto de ``elif``, ejecutándose solo el primero sea ``True``. Si tanto el ``if`` como los ``elif`` han devuelto ``False``, se ejecuta el bloque ``else``.
Existen algunos elementos adicionales que podemos usar en las secuencias de control de flujo. La sentencia **break** permite interrumpir el bloque más cercano en un bucle **while** o **for**. De manera similar, **continue** continúa con la siguiente iteración dentro del bucle más cercano. La sentencia **else en bucles** permite ejecutar una acción cuando el bucle termina una lista (en bucles **for**) o cuando la condición es falsa (con while) pero no si el bucle se interrumpe usando **break**. Veamos el ejemplo de un programa que calcula los números primos entre 2 y 10:
.. code-block:: python
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print("{} es igual a {}*{}.".format(n, x, n/x))
break # cortamos el bucle for
else:
# El bucle termina sin encontrar factor
print("{} es numero primo.".format(n))
""" Imprime:
2 es numero primo.
3 es numero primo.
4 es igual a 2*2.
5 es numero primo.
6 es igual a 2*3.
7 es numero primo.
8 es igual a 2*4.
9 es igual a 3*3.
"""
En este ejemplo usamos ``break`` para cortar el bucle for más interno si el **if se cumple** (es ``True``) y así evitar que muestre multiplicaciones equivalentes (e.g.: 3\*4 = 4\*3); podemos comprobar lo que ocurriría si no pusiésemos ``break``. Es útil para por ejemplo, evitar que un bucle siga ejecutándose si ya se cumplió la condición.
Algo muy interesante en este ejemplo es que usamos la sentencia ``else`` con ``for``, en lugar de usarla con **if** como es habitual. Un ``else`` en **for** se ejecuta **cuando la lista en el ``for`` llega al final**, sin cortarse. De esta manera imprimimos un aviso si el bucle termina (el for agota la lista) sin llegar a usar break, lo que indica que ningún número de la lista es múltiplo suyo y por tanto es primo.
La sentencia ``continue`` indica **continuar con el siguiente elemento del bucle más interior**, interrumpiendo el ciclo actual. Veamos un ejemplo que comprueba qué números son mayores que 4 en una lista de número creciente, de 0 a 7:
.. code-block:: python
for k in range(8):
if k > 4:
print("%d es mayor que 4." % k)
continue
print("%d es menor o igual que 4." % k)
0 es menor o igual que 4.
1 es menor o igual que 4.
2 es menor o igual que 4.
3 es menor o igual que 4.
4 es menor o igual que 4.
5 es mayor que 4.
6 es mayor que 4.
7 es mayor que 4.
Es este caso, con ``continue`` evitamos que se ejecute el último ``print()`` si ``k > 4``, continuando el bucle con el siguiente elemento de la lista. Así, si llegamos a ``k > 4`` es ``True``, es evidente que el siguiente elemento de la lista también lo cumplirá y se puede continuar con el siguiente elemento de ``for`` más cercano. Desde luego que pudimos haber hecho un código similar usando una sentencia if-else, pero resultaría más complejo.
Ejercicios
----------
#. La variación de temperatura de un cuerpo a temperatura inicial :math:`T_0` en un ambiente a :math:`T_s` cambia de la siguiente manera:
.. math::
T = T_s + (T_0 - T_s) \ \ e^{-kt}
con ``t`` en horas y siendo ``k`` un parámetro que depende del cuerpo (usemos ``k=0.45``). Una lata de refresco a 5ºC queda en la guantera del coche a 40ºC. ¿Qué temperatura tendrá 1, 5, 12 y 14 horas? Encontrar las horas que hay que esperar para que el cuerpo esté a 0.5ºC menos que la temperatura ambiente. Definir funciones adecuadas para realizar ambos cálculos para cualquier tiempo y cualquier diferencia de temperatura respecto al ambiente respectivamente.
#. Para el cálculo de la letra del DNI se calcula el residuo 23 del número, es decir, el resto que se obtiene de la división entera del número del DNI entre 23. El resultado será siempre un valor entre 0 y 22 y cada uno de ellos tiene asignado una letra 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``
``T R W A G M Y F P D X B N J Z S Q V H L C K E``
Escribir un programa que solicite el número de DNI al usuario y calcule la letra que le corresponde.
#. Obtener un valor de :math:`\pi` calculando la suma siguiente para n=200:
.. math::
4 \sum^n_{k=1} \frac{(-1)^{k+1}}{2k - 1}
#. Se llama sucesión de Fibonacci a la colección de *n* números para la que el primer elemento es cero, el segundo 1 y el resto es la suma de los dos anteriores. Por ejemplo, la sucesión para *n=5* es (0, 1, 1, 2, 3). Crear un programa que calcule la lista de números para cualquier *n*.
#. Genera una lista que contenga el cuadrado de los números pares y el cubo de los impares entre 1 y 100 (inclusive). Calcula cuantos números de esa lista debes sumar para que el resultado de la suma sea lo más cercano posible, pero inferior, a un millón.
#. Escribir un programa que proporcione el desglose en el número mínimo de billetes y monedas de una cantidad entera cualquiera de euros dada. Recuerden que los billetes y monedas de uso legal disponibles hasta 1 euro son de: 500, 200, 100, 50, 20, 10, 5, 2 y 1 euros. Para ello deben solicitar al usuario un número entero, debiendo comprobar que así se lo ofrece y desglosar tal cantidad en el número mínimo de billetes y monedas que el programa escribirá finalmente en pantalla.