{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Mis notas: Lo básico de Python 3

\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intro" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " * Lenguaje creado por Guido van Rossum a principios de los años 90\n", " * Semi interpretado (se genera un fichero de pseudocódigo máquina o ***bytecode*** con extensión `.pyc` que es lo que realmente se ejecuta)\n", " * Multiplataforma\n", " * Open source\n", " * Orientado a objetos\n", " * Permite programación imperativa, funcional y orientada a aspectos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Hola mundo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El hola mundo es tan sencillo como escribir una línea:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hola Mundo\n" ] } ], "source": [ "print('Hola Mundo')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python cuenta con las siguientes características destacables referentes a las variables:\n", " * Tiene **tipado dinámico**; no hay que declarar el tipo de dato que va a contener una variable (se determina en tiempo de ejecución dependiendo del valor asignado). Además puede cambiarse con otra simple asignación de valor.\n", " * Es **fuertemente tipado**; no se permite tratar a una variable como si fuera de un tipo distinto al que tiene, es necesario convertirla de forma explícita.\n", " * No hay que declarar las variables antes de asignarlas. Y si se intentan usar sin haberse asignado saltará una excepción.\n", " * Las variables son simplemente [nombres que referencian a objetos](http://foobarnbaz.com/2012/07/08/understanding-python-variables/)\n", "\n", "Por convención las variables se nombran en minúsculas y con guiones bajos para separar palabras." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "variable_zero = 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Existen 3 **tipos básicos** de variables:\n", " 1. Númericos\n", " * Enteros\n", " * `int` (sin límite de tamaño)\n", " * Reales (Decimales)\n", " * `float`. P.ej. 0.26, 0.1e-3,…\n", " * Complejos\n", " * `complex`. P.ej. 5+7j\n", " 2. Secuencias (iterables)\n", " * `str`\n", " * `bytes`\n", " * `bytearray`\n", " * `list`\n", " * `tuple`\n", " * `range`\n", " 3. Booleanos\n", " * `bool`: True, False\n", "\n", "También hay otros **tipos predefinidos**:\n", " * Mapeos (`dict`)\n", " * Ficheros\n", " * Clases\n", " * Instancias\n", " * Módulos\n", " * Excepciones\n", " * ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tipos numéricos" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 \n", "15 \n", "2.339 \n", "(1+2j) \n" ] } ], "source": [ "# A continuación unos ejemplos. Ah! esto es un comentario :) \n", "i = 2\n", "i2 = 0xf\n", "f = 2.339\n", "c = 1+2j\n", "\n", "# Para obtener el tipo de una variable cualquiera siempre podemos recurrir a type()\n", "print(i, type(i))\n", "print(i2, type(i2))\n", "print(f, type(f))\n", "print(c, type(c))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Operaciones con tipos numéricos**" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Suma: 1+1 = 2\n", "Resta: 1-1 = 0\n", "Multiplicación: 2*2 = 4\n", "División: 5/2 = 2.5\n", "División: 4.0/2 = 2.0\n", "División entera: 5//2 = 2\n", "Módulo o resto entero: 5%2 = 1\n", "Potencia: 2**3 = 8\n", "Valor absoluto: abs(-1) = 1\n" ] } ], "source": [ "print('Suma: 1+1 =', 1+1)\n", "print('Resta: 1-1 =', 1-1)\n", "print('Multiplicación: 2*2 =', 2*2)\n", "print(\"División: 5/2 =\", 5/2) # En v2 el resultado no es el esperado\n", "print(\"División: 4.0/2 =\", 4.0/2) # El resultado es siempre un float si dividendo o divisor son float\n", "print(\"División entera: 5//2 =\", 5//2)\n", "print(\"Módulo o resto entero: 5%2 =\", 5%2)\n", "print('Potencia: 2**3 =', 2**3) # Ojo, el _^_ es un XOR a nivel de bit\n", "print(\"Valor absoluto: abs(-1) =\", abs(-1))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Truncado de decimales: math.trunc(f) = 2\n", "Redondeo con n decimales: round(f, 2) = 2.34\n", "Redondeo a número entero por abajo: math.floor(f) = 2\n", "Redondeo a número entero por arriba: math.ceil(f) = 3\n" ] } ], "source": [ "import math\n", "print(\"Truncado de decimales: math.trunc(f) =\", math.trunc(f))\n", "print(\"Redondeo con n decimales: round(f, 2) =\", round(f, 2))\n", "print(\"Redondeo a número entero por abajo: math.floor(f) =\", math.floor(f))\n", "print(\"Redondeo a número entero por arriba: math.ceil(f) =\", math.ceil(f))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Comparación 1 < 2 <= 2 = True\n" ] } ], "source": [ "print(\"Comparación 1 < 2 <= 2 =\", 1 < 2 <= 2)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "AND a nivel de bit: 5&1 = 1\n", "AND a nivel de bit: 5|2 = 7\n", "XOR a nivel de bit: 5^1 = 4\n", "NOT a nivel de bit: ~1 = -2\n", "Desplazamiento dcho: 5>>1 = 2\n", "Desplazamiento izdo: 2<<1 = 4\n" ] } ], "source": [ "print(\"AND a nivel de bit: 5&1 =\", 5&1)\n", "print(\"AND a nivel de bit: 5|2 =\", 5|2)\n", "print(\"XOR a nivel de bit: 5^1 =\", 5^1)\n", "print(\"NOT a nivel de bit: ~1 =\", ~1)\n", "print(\"Desplazamiento dcho: 5>>1 =\", 5>>1)\n", "print(\"Desplazamiento izdo: 2<<1 =\", 2<<1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cadenas de caracteres\n", "Las cadenas de caracteres son un tipo de secuencia con texto definido entre comillas simples o dobles. Se pueden incluir caracteres especiales escapándolos con `\\`. Se trata de objetos inmutables e iterables." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "string \n", "äóè \n", "\\n \n", "primera linea \n", " y ésto se verá en otra \n" ] } ], "source": [ "# Distintos tipos de cadenas de caracteres\n", "s = \"string\"\n", "unicode = u\"äóè\"\n", "raw = r\"\\n\"\n", "multiline = \"\"\"primera linea \n", " y ésto se verá en otra\"\"\"\n", "\n", "print(s, type(s))\n", "print(unicode, type(unicode))\n", "print(raw, type(raw))\n", "print(multiline, type(multiline))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Operaciones con cadenas de caracteres**" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Longitud: len(s) = 6\n", "Concatenación: s + raw = string\\n\n", "Repetición: raw*3 = \\n\\n\\n\n", "Búsqueda: 'tt' not in s = True\n", "Elemento en posición i: s[2] = r\n", "Elemento en posición -i: s[-1] = g\n", "Subcadena: s[0:2] = st\n", "Subcadena con salto: s[0:6:2] = srn\n" ] } ], "source": [ "print(\"Longitud: len(s) =\", len(s))\n", "print(\"Concatenación: s + raw =\", s + raw) # mejor evitarlo\n", "print(\"Repetición: raw*3 =\", raw*3)\n", "print(\"Búsqueda: 'tt' not in s =\", \"tt\" not in s)\n", "print(\"Elemento en posición i: s[2] =\", s[2]) # los strings pueden ser tratados como listas\n", "print(\"Elemento en posición -i: s[-1] =\", s[-1])\n", "print(\"Subcadena: s[0:2] =\", s[0:2])\n", "print(\"Subcadena con salto: s[0:6:2] =\", s[0:6:2])" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Primera aparición de subcadena: 3\n", "Número de apariciones de subcadena: 1\n", "Elemento mínimo: min(s) = g\n", "Elemento máximo: max(s) = t\n" ] } ], "source": [ "print(\"Primera aparición de subcadena:\", s.index(\"ing\"))\n", "print(\"Número de apariciones de subcadena:\", s.count(\"ing\"))\n", "print(\"Elemento mínimo: min(s) =\", min(s))\n", "print(\"Elemento máximo: max(s) =\", max(s))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cadena en minúsculas: string\n", "Cadena en mayúsculas: STRING\n", "La cadena empieza por s?: True\n", "La cadena acaba por s?: True\n", "La cadena contiene la subcadena 'raw' en la posición: -1\n" ] } ], "source": [ "print(\"Cadena en minúsculas:\", s.lower())\n", "print(\"Cadena en mayúsculas:\", s.upper())\n", "print(\"La cadena empieza por s?:\", s.startswith(s))\n", "print(\"La cadena acaba por s?:\", s.endswith(s))\n", "print(\"La cadena contiene la subcadena 'raw' en la posición:\", s.find('raw'))" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cadena reemplazando s por sss: ssstring\n", "Cadena dividida con split: ['st', 'ing']\n", "Cadena re-unida con join: s-t-r-i-n-g\n", "Lista convertida en cadena con join: a b c\n", "Cadena sin espacios en los extremos: espacios everywhere\n" ] } ], "source": [ "print(\"Cadena reemplazando s por sss:\", s.replace('s', 'sss'))\n", "print(\"Cadena dividida con split:\", s.split('r'))\n", "print(\"Cadena re-unida con join:\", '-'.join(s))\n", "print(\"Lista convertida en cadena con join:\", ' '.join(['a', 'b', 'c']))\n", "print(\"Cadena sin espacios en los extremos:\", ' espacios everywhere '.strip())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Formateo de cadenas de caracteres**" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "aquí no es allí\n", "aquí no es allí, sólo es aquí\n", "Móstoles no es Madrid; es Móstoles\n" ] } ], "source": [ "print(\"{} no es {}\".format('aquí', 'allí'))\n", "print(\"{0} no es {1}, sólo es {0}\".format('aquí', 'allí'))\n", "print(\"{aqui} no es {alli}; es {aqui}\".format(aqui='Móstoles', alli='Madrid'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Formateo mejorado usando [f-strings](https://realpython.com/python-f-strings/), con evaluación de expresiones (>= Py3.6):" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Me encanta MÓSTOLES desde 1981'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aqui = 'Móstoles'\n", "f'Me encanta {aqui.upper()} desde {1980+1}'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Booleanos\n", " * Valores `True`, `False`\n", " * El tipo `bool` es subclase del tipo `int`\n", " * Operadores lógicos: `and`, `or`, `not` (textuales)\n", " * Operadores comparativos: `==`, `!=`, `<`, `>`, `<=`, `>=`" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True \n" ] } ], "source": [ "b = True\n", "print(b, type(b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Operaciones con booleanos**" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "not True: False\n", "True and not True or False: False\n", "True != False: True\n" ] } ], "source": [ "print(\"not True:\", not True)\n", "print(\"True and not True or False:\", True and not True or False)\n", "print(\"True != False:\", True != False)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True + True: 2\n", "2 == True: False\n" ] } ], "source": [ "print(\"True + True:\", True + True)\n", "print(\"2 == True:\", 2 == True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se considera `False` lo siguiente..." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "False\n", "False\n", "False\n", "False\n" ] } ], "source": [ "print(bool(None)) # None (pasado a booleano) equivale a False\n", "print(0 == True) # El 0 de cualquier tipo numérico equivale a False\n", "print(bool(\"\")) # La cadena vacía equivale a False\n", "print(bool({})) # El diccionario vacío equivale a False\n", "print(bool([])) # La secuencia vacía equivale a False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ojo con esto:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "print(bool(-2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Manipulación de variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La constante `None` equivale a *null* en otros lenguajes" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "None \n", "False\n" ] } ], "source": [ "print(None, type(None))\n", "print(0 is None) # Para comparar con None se usa is en lugar de ==" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Asignación de valores múltiple:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "x, y = 4, 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Intercambio de valores sencillo:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 4\n" ] } ], "source": [ "x, y = y, x\n", "print(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversión entre tipos explícita:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "float(2) = 2.0\n", "int(2.5) = 2\n", "2*str(2) = 22\n" ] } ], "source": [ "print('float(2) =', float(2))\n", "print('int(2.5) =', int(2.5))\n", "print('2*str(2) =', 2*str(2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Colecciones\n", "Existen muchos tipos de colecciones en Python (por ejemplo las cadenas de caracteres son secuencias, que a su vez son colecciones). Las colecciones más usadas para albergar datos de cualquier tipo son:\n", " * Listas\n", " * Tuplas\n", " * Diccionarios\n", " * Conjuntos (sets)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Listas\n", "Una lista (objeto `list`) es una colección de elementos mutable, ordenada e iterable; el equivalente a arrays o vectores en otros lenguajes. Puede contener varios tipos de datos distintos a la vez, o incluso listas anidadas.\n", "\n", "**Creación**:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "l = [9, True, 'texto', [1, 2]]\n" ] } ], "source": [ "l = [9, True, \"texto\", [1, 2]] # se pueden crear con corchetes o con la función list()\n", "print(\"l =\", l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para **acceder** a sus elementos usaremos los típicos corchetes. Como particularidades:\n", " * Podemos usar índices negativos, referidos al final de la lista.\n", " * Podemos hacer ***slicing***; seleccionar una parte de la lista usando `[inicio:fin]` o `[inicio:fin:salto]`. Si se omite el inicio o el fin se cogerá desde/hasta el extremo. También se puede usar esto para modificar una parte de la lista.\n", "\n", "NOTA: *La selección y el slicing aplica también al resto de cadenas en Python; tuplas y cadenas de caracteres.*" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "l[0] = 9\n", "l[-1][-2] = 1\n", "l[:2] = [9, True]\n", "l[::2] = [9, 'texto']\n", "l[::-1] = [[1, 2], 'texto', True, 9]\n" ] } ], "source": [ "print(\"l[0] =\", l[0])\n", "print(\"l[-1][-2] =\", l[-1][-2])\n", "print(\"l[:2] =\", l[:2])\n", "print(\"l[::2] =\", l[::2])\n", "print(\"l[::-1] =\", l[::-1]) # inversa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "También podemos crear listas con `range`. En Python 3 usamos `list(range())` para obtener una lista porque range es un generador (en Python 2 no lo era)." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4]\n", "[1, 2, 3, 4]\n", "[1, 4]\n" ] } ], "source": [ "print(list(range(5))) # rango de 0 a 5 (5 no incluido)\n", "print(list(range(1,5))) # rango de 1 a 5 (5 no incluido)\n", "print(list(range(1,5,3))) # rango de 1 a 5, cogiendo 1 de cada 3 (5 no incluido)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Manipulaciones más comunes** sobre listas (recordemos que son mutables):" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista: [9, True, 'texto', [1, 2]]\n" ] } ], "source": [ "l = [9, True, 'texto', [1, 2]]\n", "print('Lista:', l)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista después de reemplazos: [0, 1, 2, [1, 2]]\n", "Elementos de la lista: 0 1 2 [1, 2]\n" ] } ], "source": [ "# Reemplazamos elementos\n", "l[0:3] = [0, 1, 2]\n", "print('Lista después de reemplazos:', l)\n", "print('Elementos de la lista:', *l) # Con * obtenemos los elementos" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista después de appends: [0, 1, 2, [1, 2], 123, 456, 789]\n" ] } ], "source": [ "# Añadimos elementos al final\n", "l.append(123)\n", "l.append(456)\n", "l.append(789)\n", "print('Lista después de appends:', l)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista después de insert: [111, 0, 1, 2, [1, 2], 123, 456, 789]\n" ] } ], "source": [ "# Añadimos un elemento en una posición concreta desplazando el resto\n", "l.insert(0, 111)\n", "print('Lista después de insert:', l)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "l.pop() => 789\n", "l.pop(0) => 111\n", "Lista después de los pop(): [0, 1, 2, [1, 2], 123, 456]\n" ] } ], "source": [ "# Eliminamos el último elemento y el primero.\n", "print('l.pop() =>', l.pop())\n", "print('l.pop(0) =>', l.pop(0))\n", "print('Lista después de los pop():', l)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista después de del: [0, 1, 2, [1, 2], 123]\n" ] } ], "source": [ "# También podemos eliminar elemento con del (admite l[:])\n", "del l[len(l)-1]\n", "print('Lista después de del:', l)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista después de remove: [0, 1, 2, [1, 2]]\n" ] } ], "source": [ "# Eliminamos la primera aparición de un elemento concreto \n", "l.remove(123)\n", "print('Lista después de remove:', l)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Resultado de concatenación: [0, 1, 2, [1, 2], 9, 8]\n", "Lista l después de concatenación: [0, 1, 2, [1, 2]]\n" ] } ], "source": [ "# Podemos concatenar listas\n", "print('Resultado de concatenación:', l + [9, 8])\n", "print('Lista l después de concatenación:', l)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista l después de extend consigo misma: [0, 1, 2, [1, 2], 0, 1, 2, [1, 2]]\n" ] } ], "source": [ "# O extender una lista con otra (se modifica la original). Es más rápido que concatenar\n", "l.extend(l)\n", "print('Lista l después de extend consigo misma:', l)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0 in l?: True\n", " Count(0): 2\n", " index(1): 1\n", " len(l): 8\n" ] } ], "source": [ "# Comprobamos existencia de un elemento\n", "print(' 0 in l?:', 0 in l)\n", "print(' Count(0):', l.count(0))\n", "print(' index(1):', l.index(1))\n", "\n", "# Longitud de la lista\n", "print(' len(l):', len(l))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos **ordenar listas** de cadenas o numéricas, usando `list.sort()` (ordena el original) o `sorted(list)` (devuelve copia ordenada)\n", "\n", "NOTA: `sort()` es un poco más rápido y consume menos memoria; aunque sólo vale para listas, mientras que `sorted()` acepta cualquier iterable." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lista de cadenas ordenada: ['aa', 'ab']\n", "Lista numérica ordenada: [1, 2, 3]\n" ] } ], "source": [ "l_str = ['ab', 'aa']\n", "l_str.sort()\n", "print('Lista de cadenas ordenada: ', l_str)\n", "\n", "l_num = [2, 3, 1]\n", "print('Lista numérica ordenada: ', sorted(l_num))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tuplas\n", "Las tuplas (objetos `tuple`) son exactamente como las listas pero **inmutables**; una vez creadas no se pueden modificar. Proporcionan mayor eficiencia para usos más básicos.\n", "\n", "Para definirlas usaremos paréntesis (o nada) en lugar de corchetes. Para tuplas de 1 elemento es necesario poner una coma al final, para diferenciarlo de un elemento básico. Todo lo explicado para lectura de listas es también aplicable para tuplas; para acceder a sus elementos usaremos también corchetes." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 2, True, 'python')\n" ] } ], "source": [ "t1 = (1, 2, True, \"python\")\n", "print(t1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos definir tuplas con 1 sólo elemento de dos maneras:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1,) \n", "(1,) \n" ] } ], "source": [ "t2 = (1, )\n", "t3 = 1,\n", "print(t2, type(t2))\n", "print(t3, type(t3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos extraer o *desempaquetar* los valores de la tupla a variables con una asignación múltiple:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 True python\n", "1 2 True python\n" ] } ], "source": [ "t1a, t1b, t1c, t1d = t1\n", "print(t1a, t1b, t1c, t1d)\n", "print(*t1) # desempaquetado automático como argumento de una función" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 [2, True] python\n" ] } ], "source": [ "t1a, *t1b, t1c = t1\n", "print(t1a, t1b, t1d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Existe una versión ampliada de las tuplas: los objetos `namedtuple`. Nos permiten asignar nombres a los elementos, para su posterior acceso. Se pueden usar como clases sencillas inmutables:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Andrew'" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from collections import namedtuple\n", "Persona = namedtuple('Persona', ['name', 'age', 'height'])\n", "persona = Persona('Andrew', 45, 174)\n", "persona.name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diccionarios\n", "Los diccionarios (`dict`) son colecciones mutables y **sin orden** de tipo mapeado que relacionan claves y valores; lo que se conoce en otros lenguajes como mapa. Se declara con llaves y una sucesión de pares **clave : valor**. La clave puede ser de cualquier tipo inmutable (incluidas las tuplas) pero única; mientras que el valor puede ser de cualquier tipo.\n", "\n", "A los valores se accede por medio de las claves. En este caso no se puede hacer *slicing*." ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "# Creación:\n", "d = {\"Love Actually\": \"Richard Curtis\", \"Kill Bill\": \"Tarantino\"}" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(d) \n", "Movie \"Love Actually\" from director: Richard Curtis\n", "Movie \"Kill Bill\" from director: Quentin Tarantino\n" ] } ], "source": [ "# Introducción / Modificación de elemento:\n", "d[\"Kill Bill\"] = \"Quentin Tarantino\"\n", "print('type(d)', type(d))\n", "\n", "# Recorremos todos los elementos e imprimimos los pares clave-valor con formato\n", "for movie, director in d.items():\n", " print('Movie \"{}\" from director: {}'.format(movie, director))" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keys: ['Love Actually', 'Kill Bill']\n", "Values: ['Richard Curtis', 'Quentin Tarantino']\n", "Items: [('Love Actually', 'Richard Curtis'), ('Kill Bill', 'Quentin Tarantino')]\n" ] } ], "source": [ "# Obtención de claves, valores e items\n", "print(\"Keys:\", list(d.keys()))\n", "print(\"Values:\", list(d.values()))\n", "print(\"Items:\", list(d.items()))" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Love Actually in d = True\n" ] } ], "source": [ "# Comprobación de la existencia de una clave\n", "print(\"Love Actually in d =\", \"Love Actually\" in d)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d['Love Actually'] = Richard Curtis\n", "d.get(\"Love Actual\") = None\n", "d.get(\"Love Actual\", \"No encontrado\") = No encontrado\n" ] } ], "source": [ "# Obtener el valor perteneciente a una clave. EVITAR! si no existe la clave tendremos un KeyError\n", "print(\"d['Love Actually'] =\", d[\"Love Actually\"])\n", "# Obtener el valor perteneciente a una clave evitando excepción si no se encuentra.\n", "print('d.get(\"Love Actual\") = ', d.get(\"Love Actual\"))\n", "# Permite definir valor por defecto para ese caso\n", "print('d.get(\"Love Actual\", \"No encontrado\") = ', d.get(\"Love Actual\", \"No encontrado\"))" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d.get(\"Pulp Fiction\") after first setdefault() = Quentin Tarantino\n", "d.get(\"Pulp Fiction\") after second setdefault() = Quentin Tarantino\n" ] } ], "source": [ "# Introducción de un nuevo elemento, sólo si la clave no existía con anterioridad\n", "d.setdefault(\"Pulp Fiction\", \"Quentin Tarantino\")\n", "print('d.get(\"Pulp Fiction\") after first setdefault() = ', d.get(\"Pulp Fiction\"))\n", "\n", "d.setdefault(\"Pulp Fiction\", \"Quentin Tarantinoak\")\n", "print('d.get(\"Pulp Fiction\") after second setdefault() = ', d.get(\"Pulp Fiction\"))" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d.get(\"Pulp Fiction\") after del = None\n" ] } ], "source": [ "# Eliminar una entrada\n", "del d[\"Pulp Fiction\"]\n", "print('d.get(\"Pulp Fiction\") after del = ', d.get(\"Pulp Fiction\"))" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{1: 10, 2: 13, 3: 12}\n", "max(d) = 3\n", "max(d, key=d.get) = 2\n" ] } ], "source": [ "d = {1:10, 2:13, 3:12}\n", "print(d)\n", "\n", "# Obtener el elemento de un diccionario con max key\n", "print('max(d) =', max(d))\n", "\n", "# Obtener el elemento de un diccionario con max value\n", "print('max(d, key=d.get) =', max(d, key=d.get))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conjuntos\n", " Los conjuntos (`set`) son exactamente como las listas pero **sin orden**. Se crean con el método `set()` o usando llaves en lugar de corchetes.\n", "\n", " Tienen operaciones propias de los conjuntos que conocemos del mundo real: intersección, unión, diferencia, ..." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set 1: {1, 2, 3, 'abc'} \n", "Set 2: {0, 1, 2} \n", "Dict: {} \n" ] } ], "source": [ "# Dos formas de crearlos\n", "set1 = {1, 1, 2, 3, \"abc\"}\n", "set2 = set([0, 1, 2])\n", "print('Set 1:', set1, type(set1))\n", "print('Set 2:', set2, type(set2))\n", "\n", "# Ojo! con llaves y vacío sería un diccionario, no un conjunto\n", "dict = {}\n", "print('Dict:', dict, type(dict))" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set 1 después de add: {1, 2, 3, 4, 'abc'}\n", "Set 1 después de remove: {1, 2, 3, 'abc'}\n", "Set 1 después de discard: {1, 2, 3, 'abc'}\n" ] } ], "source": [ "# Añadir elementos\n", "set1.add(4)\n", "set1.add(4) # sin error!\n", "print('Set 1 después de add:', set1)\n", "\n", "# Borrar elementos\n", "set1.remove(4)\n", "print('Set 1 después de remove:', set1)\n", "\n", "# Borrar elementos sólo si existen (recomendado)\n", "set1.discard(4)\n", "print('Set 1 después de discard:', set1)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "set1 & set2: {1, 2}\n", "set1 | set2: {0, 1, 2, 3, 'abc'}\n", "set1 - set2: {3, 'abc'}\n", "set2 - set1: {0}\n", "set1 ^ set2: {0, 3, 'abc'}\n" ] } ], "source": [ "# Intersección de conjuntos con &\n", "print(\"set1 & set2:\", set1 & set2)\n", "\n", "# Unión con |\n", "print(\"set1 | set2:\", set1 | set2)\n", "\n", "# Diferencia con -\n", "print(\"set1 - set2:\", set1 - set2)\n", "print(\"set2 - set1:\", set2 - set1)\n", "\n", "# Diferencia simétrica con ^\n", "print(\"set1 ^ set2:\", set1 ^ set2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Control de Flujo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por un lado tenemos las sentencias condicionales, que se reducen a dos (el switch en Python se puede emular con un diccionario). Por otro lado están los bucles." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Condicionales" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### if... elif... else\n", " La forma más simple de crear un condicional es con un `if` seguido de la condición a evaluar, dos puntos (`:`) y en la siguiente línea e **indentado**, el código a ejecutar en caso de que se cumpla dicha condición." ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1:\n", "Positivo\n" ] } ], "source": [ "numero = 1\n", "\n", "print(\"{0}:\".format(numero))\n", "if numero < 0: \n", " print(\"Negativo\")\n", "elif numero > 0: \n", " print(\"Positivo\")\n", "else: \n", " print(\"Cero\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Asignación condicional\n", " Es el equivalente al operador ternario **?** en otros lenguajes." ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "impar\n" ] } ], "source": [ "var = \"par\" if (numero % 2 == 0) else \"impar\"\n", "print(var)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Bucles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### while\n", " Porción de código que se ejecuta mientras se cumpla una condición.\n", "\n", " La instrucción `break` nos sirve para salir del bucle. La instrucción `continue` nos llevará a la siguiente ejecución del bucle." ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Felicidades, tienes 16\n", "Felicidades, tienes 17\n", "Felicidades, tienes 18\n" ] } ], "source": [ "edad = 15\n", "while edad < 18: \n", " edad += 1\n", " print(\"Felicidades, tienes \" + str(edad))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### for... in\n", " Para iterar sobre una secuencia (cadenas, iterables como range(), d.keys(), iterators, etc). Lo bueno es que itera sobre los elementos, no sobre las posiciones." ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Elemento uno leído\n", "Elemento dos leído\n", "Elemento tres leído\n" ] } ], "source": [ "# Iterando sobre una secuencia\n", "secuencia = ['uno', 'dos', 'tres']\n", "for elemento in secuencia: \n", " print('Elemento {} leído'.format(elemento))" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n" ] }, { "data": { "text/plain": [ "dict_keys" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Iterando sobre un iterable\n", "for elemento in d.keys():\n", " print(elemento)\n", "type(d.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos iterar con `enumerate` para acceder a la vez al elemento y al índice:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Elemento 0 leído: uno\n", "Elemento 1 leído: dos\n", "Elemento 2 leído: tres\n" ] } ], "source": [ "for i, elemento in enumerate(secuencia): \n", " print('Elemento {} leído: {}'.format(i, elemento))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Iteradores\n", "\n", "Un **iterador** es un iterable sobre el que se puede aplicar la función `__next__()` para obtener el siguiente elemento, guardando el estado." ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "# El iterador se crea a partir de un iterable\n", "iterator = iter(d.keys())\n", "print(type(iterator))" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n" ] } ], "source": [ "# Podemos acceder al siguiente elemento con iterator.__next__() o next(iterator)\n", "print(iterator.__next__())\n", "print(next(iterator))" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Elemento en for: 3\n" ] } ], "source": [ "# O podemos usarlo directamente en un bucle for, porque un iterador es un iterable\n", "for elemento in iterator:\n", " print(\"Elemento en for: \", elemento)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OJO: obtendremos una excepción si intentamos obtener el siguiente elemento y no hay más." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Borrado iterativo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Para manipular los elementos de una secuencia con operaciones como puede ser el borrado, recorreremos una copia del original, de tal forma que al borrar un elemento del original seguirá estando en la copia y el bucle no se saltará nada. Para la copia real podemos hacer `copia = secuencia[:]` o `copia = (secuencia)`" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n", "[2, 3]\n", "[3]\n", "[]\n" ] } ], "source": [ "secuencia = [1, 2, 3]\n", "print(secuencia)\n", "for elemento in secuencia[:]:\n", " secuencia.remove(elemento) # borrado sobre el original\n", " print(secuencia)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ¿Qué ocurre si no trabajamos con una copia? al borrar el 1º, el 2º pasa a ser el 1º, por lo que no procesamos el 2º" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n", "[2, 3]\n", "[2]\n" ] } ], "source": [ "secuencia = [1, 2, 3]\n", "print(secuencia)\n", "for elemento in secuencia:\n", " secuencia.remove(elemento)\n", " print(secuencia)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Excepciones\n", " En python se usa una construcción `try`-`except`-`else`-`finally` para capturar y tratar las excepciones." ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "La variable no es correcta\n", "Esto se ejecuta siempre\n" ] } ], "source": [ "# Ejemplo:\n", "try:\n", " num = int(\"3a\")\n", " print(\"Hecho!\")\n", "except (NameError, ValueError) as e:\n", " print(\"La variable no es correcta\")\n", "except:\n", " print(\"Error\")\n", "else:\n", " print(\"Esto se ejecuta cuando no hay excepciones\")\n", "finally:\n", " print(\"Esto se ejecuta siempre\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Más adelante veremos cómo crear nuestras propias excepciones.\n", "\n", " Podemos usar `assert` si queremos testear algo y en caso de que no se cumpla automáticamente se lance una excepción de tipo `AssertionError`." ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "assert len(l) > 0, \"La secuencia debe contener elementos\" # Raises exception if empty" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para lanzar una excepción de forma manual usaremos `raise()`:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Excepción!\n" ] } ], "source": [ "try:\n", " raise(Exception)\n", "except:\n", " print(\"Excepción!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Funciones\n", " Se conoce así a los fragmentos de código con nombre que devuelven un resultado. Se usa `def` para definirlas y `return` para devolver valores o tuplas. Si no especificamos un valor de retorno, la función devolverá por defecto `None` (el equivalente en Python para *null*)." ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "# Definimos una función de primer orden, con un valor por defecto para el 2º parámetro\n", "def imprimir1(texto, veces=1):\n", " \"\"\"Esta funcion imprime los dos valores pasados \n", " como parametros\"\"\" # Docstring: lo que imprime el operador ? de Python o la función help\n", " \n", " print(texto*veces)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hola\n", "hola hola \n", "hola hola \n", "None\n" ] } ], "source": [ "# Ejecutamos la función de varias formas posibles\n", "imprimir1(\"hola\")\n", "imprimir1(\"hola \", 2)\n", "res = imprimir1(veces = 2, texto = \"hola \")\n", "print(res)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos crear una función con un **número variable de parámetros posicionales** o sin clave, precediendo el último de un asterisco (`*args`). Eso rellenará una tupla con los valores pasados:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "holahola mario luis\n" ] } ], "source": [ "def imprimir2(texto, veces=1, *otros):\n", " print(texto*veces, *otros)\n", " \n", "imprimir2(\"hola\", 2, \"mario\", \"luis\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "También existe la opción de poner dos asteriscos (`**kwargs`) para recibir un **número indeterminado de parámetros con clave**. Esto rellenará un diccionario en lugar de una tupla." ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "holahola mario luis\n" ] } ], "source": [ "def imprimir3(texto, veces=1, **otros):\n", " print(texto*veces, *list(otros.values()))\n", " \n", "imprimir3(\"hola\", 2, invitado1 = \"mario\", invitado2 = \"luis\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos combinar ambas opciones. Pondremos primero los parámetros posicionales y luego los que van con clave." ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3 11 12\n" ] } ], "source": [ "def imprimir4(*args, **kwargs):\n", " print(*args, *list(kwargs.values()))\n", "\n", "args1 = (1, 2, 3)\n", "args2 = {'a': 11, 'b': 12}\n", "imprimir4(*args1, **args2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "De forma análoga podemos detallar los argumentos y desempaquetarlos de una tupla o un diccionario al llamar a la función." ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1\n", "1 2\n" ] } ], "source": [ "def func(a, b):\n", " print(a, b)\n", "\n", "t = (0, 1)\n", "func(*t)\n", "\n", "d = {'a': 1, 'b': 2}\n", "func(**d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En Python a veces se dice que las variables mutables se pasan a las funciones como referencia, y las inmutables como valor; basándose en que las primeras se pueden modificar dentro de la función teniendo efecto fuera, y las segundas no. Esto puede llevar a confusión. En Python **no existe el paso como referencia *per se***. Lo probamos con una función que intente intercambiar 2 variables:" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "def swap(a, b):\n", " (a, b) = (b, a) # swap sencillo en Python\n", " return (a, b)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(2, 1)\n", "1 2\n" ] } ], "source": [ "# Probamos con variables inmutables\n", "a, b = 1, 2\n", "print(swap(a, b))\n", "print(a, b)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "([2], [1])\n", "[1] [2]\n" ] } ], "source": [ "# Ahora probamos con variables mutables (listas)\n", "a, b = [1], [2]\n", "print(swap(a, b))\n", "print(a, b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como podemos comprobar no funciona en ningún caso (las variables se mantienen igual fuera de la función). Pero si queremos conseguir el efecto del paso de argumentos como referencia, tenemos [varias opciones](https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference):\n", " 1. devolver una tupla y reasignarla a las variables originales\n", " 2. usar variables globales (no recomendado)\n", " 3. pasar como parámetro una variable que referencie a un objeto mutable. Podremos cambiar su contenido (si no cometemos el error de reasignar la variable a otro objeto):" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0]\n", "[0, 0]\n", "[0, 0]\n" ] } ], "source": [ "def append_0(l):\n", " l.append(0)\n", " return l\n", "\n", "l=[]\n", "print(append_0(l))\n", "print(append_0(l))\n", "print(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Más adelante veremos funciones de orden superior." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Orientación a objetos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Partimos de que en Python *todo es un objeto*.\n", "\n", " **Comparación de objetos**\n", "\n", " Para comparar el ***valor*** de 2 objetos usamos el operador `==`, mientras que para comparar su ***identidad*** usaremos `is`" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a == b: True\n", "a is b: False\n" ] } ], "source": [ "# Variables inmutables\n", "a = 257\n", "b = 257\n", "print('a == b: ', a == b)\n", "print('a is b: ', a is b)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a == b: True\n", "a is b: True\n" ] } ], "source": [ "# Excepción: enteros pequeños (se cachean)\n", "a = 256\n", "b = 256\n", "print('a == b: ', a == b)\n", "print('a is b: ', a is b)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a == b: True\n", "a is b: False\n" ] } ], "source": [ "# Variables mutables\n", "a = [1]\n", "b = [1]\n", "print('a == b: ', a == b)\n", "print('a is b: ', a is b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Copia de objetos**\n", "\n", "Cuando hacemos una copia de un objeto inmutable, obtenemos una copia real o *\"profunda\"* del mismo." ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 1\n", "b = a\n", "a += 1\n", "b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cuando hacemos una copia de un objeto mutable en Python, obtenemos una copia de la referencia a su espacio en memoria. Esto se conoce como copia *\"superficial\"*." ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4]\n" ] } ], "source": [ "ids = [1, 2, 3]\n", "ids2 = ids\n", "ids.append(4)\n", "\n", "print(ids2)" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n" ] } ], "source": [ "# Ejemplo de cómo crear una copia real o \"profunda\" de un objeto mutable\n", "ids = [1, 2, 3]\n", "ids2 = ids.copy() # o ids[:] por ser una lista\n", "ids.append(4)\n", "\n", "print(ids2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Clases e instancias\n", "Las clases en Python se declaran como en el siguiente ejemplo. Distinguiremos entre atributos de clase y variables de instancia, y presentamos tres tipos de métodos (de instancia, de clase, estáticos):" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [], "source": [ "class Coche: \n", " \"\"\"Abstraccion de los objetos coche.\"\"\" # docstring\n", " \n", " ruedas = 4 # atributo público de clase, compartido por todas sus instancias\n", " \n", " # Método especial (constructor). Función que se ejecuta al instanciar un nuevo objeto de la clase.\n", " def __init__(self, marca):\n", " self.marca = marca # argumento del objeto -> atributo de la instancia\n", " \n", " # Métodos de instancia (self,). Acceden a cosas de la instancia por medio de self.( )\n", " def get_marca(self): \n", " return self.marca\n", " \n", " def pintar(self, color): \n", " print(\"Pintar de \", color)\n", " \n", " # Método de clase (cls,). Compartido con todas las instancias. Acceden a cosas de la clase por medio de cls.( )\n", " @classmethod\n", " def get_ruedas(cls):\n", " return cls.ruedas\n", " \n", " # Método estático (). No tiene acceso a la instancia o el objeto.\n", " @staticmethod\n", " def cerrar():\n", " print(\"Cerrado\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " El primer parámetro de todos los métodos de instancia será `self`, aunque no hay que escribirlo al hacer las llamadas, ya que lo pone Python automáticamente.\n", " Los atributos de la clase serán accedidos desde la propia clase como *self.variable* y se pueden modificar dentro de cualquier función." ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ruedas de mi_coche: 4\n", "Ruedas de Coche: 4\n", "Ruedas de mi_coche: 4\n", "Ruedas de Coche: 4\n", "Marca de mi_coche: Seat\n", "Marca de mi_coche: Seat\n", "Cerrado\n", "Cerrado\n", "\n", "\n" ] } ], "source": [ "# Creación de un objeto, instancia de la clase\n", "mi_coche = Coche('Seat')\n", "\n", "# Acceso a atributo de clase\n", "print('Ruedas de mi_coche:', mi_coche.ruedas)\n", "print('Ruedas de Coche:', Coche.ruedas)\n", "\n", "# Acceso a método de clase\n", "print('Ruedas de mi_coche:', mi_coche.get_ruedas())\n", "print('Ruedas de Coche:', Coche.get_ruedas())\n", "\n", "# Acceso a variable de instancia\n", "print(\"Marca de mi_coche:\", mi_coche.get_marca()) # Coche.get_marca() => error\n", "print(\"Marca de mi_coche:\", mi_coche.marca)\n", "\n", "# Acceso a método estático\n", "mi_coche.cerrar()\n", "Coche.cerrar()\n", "\n", "print(type(Coche)) #Object\n", "print(type(mi_coche)) #Coche" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para comprobar si un objeto tiene un atributo concreto tenemos la función `hasattr()`. Para obtener su valor usaremos `getattr()`. Estos métodos son muy útiles cuando no conocemos el nombre del atributo a priori o cuando estamos dentro de un bucle accediendo a varios atributos." ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "False\n", "4\n" ] } ], "source": [ "print(hasattr(mi_coche, 'ambientador'))\n", "print(getattr(mi_coche, 'ruedas'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Los métodos especiales son aquellos métodos privados que Python nos proporciona para cada objeto, y cuyo nombre empieza y acaba por `__`. Los podemos usar de forma interna. A continuación una lista de los métodos especiales disponibles en una clase de nueva creación." ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']\n" ] } ], "source": [ "class NuevaClase:\n", " pass\n", "\n", "# Podemos imprimir las funciones de cualquier objeto con dir()\n", "print(dir(NuevaClase))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para evitar inicializaciones y liberar memoria en una tarea típica (por ejemplo leer de un fichero) se usa la orden `with`. Python ejecutará el método `__enter__()` del objeto obtenido antes del bloque a continuación, y `__exit__()` al acabar dicho bloque. Un ejemplo con el objeto file que ya tiene esos métodos implementados:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "try: \n", " with open('file.txt', 'r') as f:\n", " for line in f:\n", " print(line)\n", "except FileNotFoundError:\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Herencia\n", " Para indicar que una clase hereda de otra se coloca el nombre de la clase padre entre paréntesis cuando declaramos la clase hija.\n", "\n", " En Python lo más destacado es que se permite la **herencia múltiple**, por lo que no son necesarias las interfaces como en otros lenguajes.\n", "\n", " La clase hija hereda los atributos y las funciones de las clases padre referenciadas (pudiendo sobrescribirlos), y si hay alguna función cuyo nombre se repita en varias clases padre, tendrá preferencia la primera clase que aparece en la declaración.\n", "\n", " Si la clase hija no define un método `__init__()`, se llamará automáticamente al de la clase padre; aunque lo adecuado es definirlo y llamarlo explícitamente." ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'batera'" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Clase padre\n", "class Instrumento:\n", " def __init__(self, nombre):\n", " self.nombre = nombre\n", " def get_nombre(self):\n", " return self.nombre\n", "\n", "# Clase hija\n", "class Bateria(Instrumento):\n", " def __init__(self, nombre, platillos):\n", " super().__init__(nombre) # si sólo hay una clase padre\n", " # Instrumento.__init__(self, nombre) # si hay más de una clase padre\n", " self.platillos = platillos\n", " \n", "mi_bateria = Bateria(\"batera\", 13)\n", "mi_bateria.get_nombre()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podremos comprobar en cualquier momento si un objeto pertenece realmente a la clase hija o a la padre usando `type`:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "isinstance: Instrumento\n", "isinstance: Bateria\n", "type: Bateria\n" ] } ], "source": [ "if isinstance(mi_bateria, Instrumento):\n", " print('isinstance: Instrumento')\n", "if isinstance(mi_bateria, Bateria):\n", " print('isinstance: Bateria')\n", "if type(mi_bateria) is Instrumento:\n", " print('type: Instrumento')\n", "if type(mi_bateria) is Bateria:\n", " print('type: Bateria')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Notas sobre polimorfismo y encapsulamiento\n", " **No existe sobrecarga de métodos en la misma clase** debido al tipado dinámico de Python. De intentarse usar como en otros lenguajes, el último método definido sobrescribiría los anteriores. Pero podemos conseguir el mismo efecto jugando con parámetros de longitud variable, valores por defecto de los mismos y decoradores.\n", "\n", " **No existen los modificadores de acceso**. Lo que hace Python es considerar privada toda aquella función o variable que empiece por `__` (siempre que no acabe igual, siendo entonces una *función especial*), o protegida en caso de ir precedida por un único `_`. En el resto de casos la función o variable será pública." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Excepciones propias\n", " En Python podemos crear (y lanzar) nuestras propias excepciones. Basta con crear una clase que herede de `Exception` o cualquiera de sus hijas. Las excepciones como ya hemos visto antes se crean con `raise` y se recogen con `except`:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error 33\n" ] } ], "source": [ "class MiError(Exception):\n", " def __init__(self, valor):\n", " self.valor = valor\n", " def __str__(self):\n", " return \"Error \" + str(self.valor)\n", "\n", "try:\n", " if 22 > 20:\n", " raise MiError(33)\n", "except MiError as e:\n", " print(e) # o por ejemplo pass si no queremos tratarla" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Metaclases\n", " ¿Qué es una metaclase? Pues es una clase cuyas instancias son clases en lugar de objetos. Es decir; si para construir un objeto se usa una clase, para construir una clase se utiliza una metaclase (por defecto `type`)." ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "\n" ] } ], "source": [ " mi_coche = type('Coche',(),{'gasolina':3})\n", " print(mi_coche.gasolina)\n", " print(type(mi_coche))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Módulos y paquetes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Módulos\n", " Los módulos son entidades que permiten organizar y dividir lógicamente nuestro código cuando tenemos programas demasiado grandes. Los **ficheros** son su equivalente en el mundo físico.\n", "\n", " Para usar la funcionalidad definida en un módulo, tendremos que importarlo con `import` + nombre del módulo sin extensión de fichero. Esto no sólo deja la funcionalidad disponible, sino que **ejecuta** dicho módulo. Podemos escribir varios módulos separados por comas en la instrucción import.\n", "\n", " Para usar funciones de los módulos importados, habrá que antecederlas del nombre del módulo y un punto. O podemos usar `from [module] import [function]` para importar el objeto al espacio de nombres actual y así ahorrarnos escribir el nombre del módulo. También es posible usar `from [module] import *` pero se desaconseja.\n", "\n", " El atributo `__doc__` nos sirve para documentar el módulo." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Opciones de importación**\n", "\n", "```ipython\n", "from math import * # NO!!!!! importa todas las funciones del módulo\n", "import math # importa el módulo; ejecución como math.sqrt()\n", "import math as M # importa el módulo usando alias; ejecución como M.sqrt()\n", "from math import sqrt, cos # SI!!!!! importa funciones concretas del módulo\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Para importar módulos en otro directorio distinto al nuestro, deberemos tenerlos disponibles en la variable **PYTHONPATH**. Podemos consultar el contenido del path en python ejecutando lo siguiente:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['C:\\\\Users\\\\yago_\\\\Dropbox\\\\DEV\\\\projects\\\\notebooks\\\\python3-101', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\python37.zip', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\DLLs', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37', '', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib\\\\site-packages', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib\\\\site-packages\\\\win32', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib\\\\site-packages\\\\win32\\\\lib', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib\\\\site-packages\\\\Pythonwin', 'C:\\\\Users\\\\yago_\\\\Miniconda3\\\\envs\\\\py37\\\\lib\\\\site-packages\\\\IPython\\\\extensions', 'C:\\\\Users\\\\yago_\\\\.ipython']\n" ] } ], "source": [ "import sys\n", "print(sys.path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Podemos imprimir las funciones y atributos de un módulo usando `dir()`" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']\n" ] } ], "source": [ "print(dir(math))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Los módulos (ficheros) son también objetos, por lo que pueden tener sus atributos y sus métodos. El atributo `__name__` se usa de forma habitual en un módulo para comprobar si se está ejecutando el módulo como importado, o bien como programa principal (en cuyo caso `__name__` será igual a \"\\_\\_main\\_\\_\", pudiendo ejecutar condicionalmente una parte del código):" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ésto se imprime siempre\n", "Ésto se imprime sólo cuando la ejecución no es mediante import\n" ] } ], "source": [ "print(\"Ésto se imprime siempre\")\n", "\n", "if __name__ == \"__main__\":\n", " print(\"Ésto se imprime sólo cuando la ejecución no es mediante import\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Paquetes\n", " Los paquetes sirven para organizar los módulos. En realidad ambos son tipos especiales de módulos (`module`). Los paquetes se representan físicamente como **directorios**.\n", "\n", " Para hacer que python trate un directorio como un paquete es necesario crear un fichero `__init__.py` dentro del mismo. En dicho fichero se definen elementos que pertenezcan al paquete, aunque basta con meter un módulo en el directorio para que esté disponible.\n", "\n", " Como se trata de módulos, para tenerlos disponibles se usa `import`:" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [], "source": [ "# import paquete.subpaquete.modulo\n", "\n", "# modulo.func()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Programación funcional" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La programación funcional es un paradigma en el que el código se basa casi en su totalidad en funciones, entendiendo el concepto de función según su definición matemática, y no como los simples subprogramas de los lenguajes imperativos que podamos haber visto hasta ahora. El concepto de variables desaparece, y las funciones no tienen efectos colaterales; el resultado de ejecutar una función 2 veces con la misma entrada será el mismo, con todas las ventajas que eso supone.\n", "\n", " Python cuenta con varias características de este paradigma.\n", "\n", "### Funciones de orden superior\n", " Las funciones en Python son de primera clase o de orden superior. Como todo en Python las funciones son objetos: pueden asignarse a una variable o guardarse en una estructura, y pueden pasarse como parámetro a otras funciones, o devolverse como resultado de las mismas." ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13\n" ] } ], "source": [ "# Ejemplo\n", "def crear_suma(x):\n", " def suma(y):\n", " return x + y\n", " return suma\n", "\n", "suma_10 = crear_suma(10)\n", "\n", "print(suma_10(3))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hola\n" ] } ], "source": [ "# Ejemplo más práctico\n", "def saludar(lang):\n", " \n", " def saludar_es():\n", " print(\"Hola\")\n", " def saludar_en():\n", " print(\"Hello\")\n", " def saludar_it():\n", " print(\"Ciao\")\n", "\n", " lang_func = {\"es\": saludar_es,\n", " \"en\": saludar_en,\n", " \"it\": saludar_it}\n", " \n", " return lang_func[lang]\n", " \n", "f = saludar(\"es\") # devuelve una función\n", "f() # ejecutamos la función\n", "\n", "# Podríamos simplificar escribiendo: saludar(\"es\")()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Iteraciones de orden superior sobre listas\n", " Podemos pasar nuestras funciones de orden superior como argumentos a las funciones del core `map` y `filter`, u otras como `reduce`. Estas funciones nos permiten precindir de los bucles típicos de otros lenguajes.\n", "\n", " #### map(function, iterable[, iterable, ...])\n", "\n", " Devuelve una secuencia (objeto map) con el resultado de aplicar una función a cada elemento de un iterable (o varios iterables, uno a uno). Si se pasan como parámetros n iterables, la función tendrá que aceptar n argumentos. Si alguna de las secuencias es más pequeña que las demás, el valor que le llega a la función para posiciones mayores que el tamaño de dicha secuencia será None." ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[1, 4, 9]\n" ] } ], "source": [ "# Ejemplo\n", "def cuadrado(n):\n", " return n ** 2\n", " \n", "l = [1, 2, 3]\n", "l2 = map(cuadrado, l)\n", "\n", "print(l2)\n", "print(list(l2))" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['appleorange', 'bananalemon', 'cherrypineapple']\n" ] } ], "source": [ "# Ejemplo con 2 iterables\n", "def concat_zip(a, b):\n", " return a + b\n", "\n", "x = map(concat_zip, ('apple', 'banana', 'cherry'), ('orange', 'lemon', 'pineapple')) \n", "print(list(x))\n" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['1', '2', '3']\n" ] } ], "source": [ "# Ejemplo: convertir lista de números en lista de caracteres\n", "print(list(map(str, l)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### filter(function, iterable)\n", "\n", " Devuelve una secuencia con los elementos del iterable para los que la función devuelve `True`." ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[2, 4, 6]\n" ] } ], "source": [ "# Ejemplo\n", "def es_par(n):\n", " return (n % 2.0 == 0)\n", "\n", "l = [1, 2, 3, 4, 5, 6, 7]\n", "f = filter(es_par, l)\n", "print(f)\n", "print(list(f))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### reduce(function, iterable[, initial])\n", "\n", "Devuelve el resultado (un valor) de ir aplicando una función a pares de elementos de un iterable. La función aceptará 2 parámetros; el primero es el valor acumulado de la ejecución anterior (initial si es la primera) y el segundo es el elemento actual del iterable. En Python 3 forma parte de **functools**" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "120\n" ] } ], "source": [ "from functools import reduce\n", "\n", "def multiplica(x, y):\n", " return x * y\n", "\n", "print(reduce(multiplica, [1, 2, 3, 4, 5]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Funciones lambda\n", " Las funciones lambda son funciones anónimas o temporales, que no podrán ser referenciadas más adelante.\n", "\n", " Se construyen mediante el operador lambda, los _parámetros_ de la función separados por comas (**SIN** paréntesis), _dos puntos_ (:) y el _código_ de la función." ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n" ] } ], "source": [ "# Ejemplo simple\n", "print((lambda x: x % 2)(5))" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2]\n" ] } ], "source": [ "# Ejemplo con filter\n", "lista = [1, 2, 3]\n", "print(list(filter(lambda n: n % 2.0 == 0, lista)))\n", "\n", "# La función lambda equivale a:\n", "def lambda_function(n):\n", " return n % 2.0 == 0" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]\n" ] } ], "source": [ "# Ejemplo con sort (función que ordena un diccionario atendiendo a una clave):\n", "points = [{\"x\": 2, \"y\": 3}, {\"x\": 4, \"y\": 1}]\n", "points.sort(key=lambda i: i[\"y\"]) # [{\"y\":1, ..}, {\"y\":3, ..}]\n", "print(points)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Comprensión de listas\n", " Construcción que permite crear listas a partir de otras listas. También es aplicable a otros iterables, aunque su uso más habitual es con listas.\n", " \n", " Su estructura puede seguir dos patrones:\n", "```\n", " [function(x) for x in iterable [if condition]]\n", "\n", " [function(x) if condition [else operation2] for x in iterable]\n", "```\n", " Cada una de estas construcciones consta de una expresión que determina cómo modificar el elemento de la lista original, seguida de una o varias cláusulas for y opcionalmente una o varias cláusulas if." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ejemplos**" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4, 5, 6, 7]\n" ] } ], "source": [ "print(l)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 4, 9, 16, 25, 36, 49]\n" ] } ], "source": [ "# Ejemplo equivalente a map\n", "print([n ** 2 for n in l])" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[2, 4, 6]\n" ] } ], "source": [ "# Ejemplo equivalente a filter\n", "print([n for n in l if n % 2.0 == 0])" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 2, 0, 4, 0, 6, 0]\n" ] } ], "source": [ "# Ejemplo con if-else\n", "print([n if n % 2 == 0 else 0 for n in l]) # cambia los impares por 0" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "# Ejemplo con doble for para obtener las combinaciones que suman 10\n", "print(sum([1 if l[i] + l[j] == 10 else 0 for i in range(len(l)) for j in range(i+1,len(l))]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Comprensión de conjuntos**" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{4, 5, 6, 7, 8}\n" ] } ], "source": [ "nombres = ['jaime', 'yago', 'iago', 'tiago', 'diego', 'jacobo', 'iacobus', 'santiago']\n", "longitudes = {len(nombre) for nombre in nombres}\n", "print(longitudes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Comprensión de diccionarios**" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'jaime': 5, 'yago': 4, 'iago': 4, 'tiago': 5, 'diego': 5, 'jacobo': 6, 'iacobus': 7, 'santiago': 8}\n" ] } ], "source": [ "name_lengths = {nombre: len(nombre) for nombre in nombres}\n", "print(name_lengths)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Generadores\n", " Los generadores son una herramienta de programación *perezosa*, con una expresión similar a la de la comprensión de listas (de hecho se escriben igual que éstas pero usando paréntesis en lugar de corchetes). La diferencia es que no devuelven una lista, sino un generador.\n", "\n", " Un generador es un tipo especial de función que genera bajo demanda valores sobre los que iterar. Para devolver el siguiente valor sobre el que iterar se usa la palabra clave `yield` en lugar de return. Para iterar sobre el generador se usa por ejemplo un for...in\n", "\n", " Como no se llega a crear una lista en memoria, sino que se generan valores y se consumen, estamos ahorrando recursos; algo que notaremos con grandes cantidades de datos. No obstante podemos crear una lista a partir de un generador gracias a la función `list()`." ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "2\n", "4\n", "6\n" ] } ], "source": [ "# Ejemplo\n", "def mi_generador(n, m, s):\n", " while(n <= m):\n", " yield n\n", " n += s\n", " \n", "for n in mi_generador(0, 7, 2):\n", " print(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Clausuras\n", "\n", "Una clausura (*closure*) es un mecanismo para llamar a una función interna que tiene acceso al *scope* de su función contenedora" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10.0\n", "12.5\n", "15.0\n" ] } ], "source": [ "# Ejemplo de función para calcular la media de una serie\n", "def construir_calculadora_media():\n", " series = [] # variable local en el ámbito de la función outer, accesible desde la inner\n", " def calcular_media(valor):\n", " series.append(valor)\n", " return sum(series)/len(series)\n", " return calcular_media\n", "\n", "calcular_media = construir_calculadora_media() # clausura de la función inner\n", "print(calcular_media(10))\n", "print(calcular_media(15))\n", "print(calcular_media(20))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " ### Decoradores\n", " Un decorador es una función que recibe otra función como parámetro y extiende el comportamiento de aquella sin modificarla. Devuelve una función interna como resultado.\n", "\n", " Puede verse como un recubrimiento para funciones; útil por ejemplo para debugging, ejecución con reintentos, tratamiento de excepciones, etc." ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Llamada a la función imprimir5\n", "hola!\n" ] } ], "source": [ "# Ejemplo\n", "def mi_decorador(funcion):\n", " def nueva(*args):\n", " print(\"Llamada a la función\", funcion.__name__)\n", " return funcion(*args)\n", " return nueva\n", " \n", "def imprimir5(texto):\n", " print(texto)\n", "\n", "mi_decorador(imprimir5)(\"hola!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si queremos algo más limpio y que el decorador se aplique siempre a la función, lo escribiremos como una anotación delante:" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Llamada a la función imprimir6\n", "hola!\n" ] } ], "source": [ "@mi_decorador\n", "def imprimir6(texto):\n", " print(texto)\n", "\n", "imprimir6(\"hola!\")" ] } ], "metadata": { "file_extension": ".py", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.5" }, "mimetype": "text/x-python", "name": "python", "npconvert_exporter": "python", "pygments_lexer": "ipython3", "toc": { "base_numbering": "0", "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Mis notas: Lo básico de Python 3", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": true }, "version": 3 }, "nbformat": 4, "nbformat_minor": 2 }