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.