Fundamentos_Python_v0.1.4
Fundamentos_Python_v0.1.4
Fundamentos de programación en
Python
Qué es Python 6
Plataformas de desarrollo 6
Tipos de datos 7
Variables y constantes 7
Listas 12
Operadores y expresiones 18
Operadores relacionales 18
Operadores lógicos 20
Expresiones anidadas 21
Operadores de asignación 24
Control de flujo 26
Sentencia if 26
Sentencia while 30
Sentencia for 34
Colecciones 38
Tuplas 39
Conjuntos 41
Diccionarios 45
Pilas 49
2
Colas 50
Salida de datos 56
Funciones 60
Definición de funciones 60
Retorno de valores 63
Enviando valores 65
Argumentos y parámetros 66
Argumentos indeterminados 70
Funciones recursivas 71
Manejo de excepciones 76
Errores 76
Excepciones 79
Múltiples excepciones 81
Invocación de excepciones 84
Clases y objetos 84
Clases y objetos 91
Métodos especiales 96
3
Encapsulación de atributos 99
Herencia 101
Módulos 120
Collections 122
Datetime 124
Math 127
Random 129
Paquetes 132
Creación 134
Modificación 136
Pandas 139
4
Visualización con Matplotlib 142
Ejemplo esperanza de vida frente a renta per cápita por países 149
NumPy 165
5
Introducción
A lo largo de este módulo el alumno aprenderá los fundamentos más básicos
del lenguaje de programación Python. Para ello se podrán realizar varios
ejercicios en cada sección de modo que el aprendizaje sea gradual y efectivo,
pues el alumno comenzará a programar desde el primer momento. Al final del
módulo se introducirán algunos conceptos básicos y librerías utilizadas en el
tratamiento de datos y cálculo numérico como son Pandas y NumPy.
Qué es Python
Python es un lenguaje de programación interpretado con licencia de código
abierto y con una curva de aprendizaje muy sencilla. Se creó a finales de los
años 80 por Guido Van Rossum. Actualmente es uno de los lenguajes más
populares y utilizados en la industria tecnológica. Tiene una sintaxis que hace
que el código sea muy legible. Soporta la Orientación a Objetos y
programación imperativa y en menor medida programación funcional. Para la
realización de este módulo se ha utilizado Python en su versión 3.
Plataformas de desarrollo
Se recomienda usar cualquier editor de texto, pero existen IDEs como Atom,
Sublime o Visual Studio Code que nos permitirán programar en Python con
mayor facilidad, ya que disponen de varias herramientas que nos pueden
servir para comprobar la sintaxis y detectar los posibles errores del código
automáticamente. Para el aprendizaje del lenguaje es altamente recomendable
utilizar algún entorno de desarrollo como Anaconda, ya que incorpora una
aplicación web llamada Jupiter Notebook que nos puede venir muy bien para
practicar y realizar pruebas con nuestro código de una manera muy fácil y
sencilla.
6
Tipos de datos
Variables y constantes
En cambio las constantes se suelen escribir con todas las letras mayúsculas y
si se componen de varias palabras, estas separadas por guión bajo:
Hay un tipo de datos para los números, concretamente para los números
enteros y los flotantes o decimales. El intérprete de Python permite realizar con
este tipo de datos cálculos simples como sumar, restar, multiplicar, dividir,
operaciones módulo o potencias. Algunos ejemplos:
7
>>> resta_negativa = 5-
9
>>> resta_negativa
-4
>>> multiplicacion = 3*
4
>>> multiplicación
12
>>> multiplicacion_decimal = 0.3*4
>>> multiplicacion_decimal
1.2
>>> division = 28/7
>>> division
4.0
>>> operacion_modulo = 25%5
>>> operacion_modulo
0
>>> potencia = 3**2
>>> potencia
9
Se pueden realizar operaciones concatenadas más complejas. El orden
correcto siempre será de izquierda a derecha, primero las multiplicaciones y
divisiones, y luego las sumas y restas. Python se encargará de respetar el
orden de los operadores automáticamente, por ejemplo:
Y por supuesto se puede hacer operaciones con tan solo el nombre de las
variables, de modo que se utilizará los valores que estas tengan almacenado
en memoria.
Otro tipo de datos son las cadenas de caracteres o string. Estas cadenas se
pueden escribir dentro de unas comillas dobles (") o simples ('), por ejemplo:
8
Los dos ejemplos anteriores muestran un resultado por pantalla cuando se
escribe una cadena de texto entre las comillas dobles o simples en el
intérprete de Python, pero para poder tratar mejor estas cadenas de
carácteres usaremos una función propia de Python llamada print(). No solo
nos permitirá mostrar por pantalla el mensaje de texto que estamos
escribiendo, si no que además es capaz de interpretar caracteres especiales
como son las tabulaciones \ t, los saltos de línea \ n y otros muchos más, de
momento veremos estos dos, por ejemplo:
>>> print('Hola\tmundo!')
Hola mundo!
>>> print('Hola\nmundo!')
Hola
mundo!
>>> print(r'C:\nombre\de\un\directorio')
C:\nombre\de\un\directorio
Otra opción es escapar los caracteres que no queremos que se procesen, para
ello se utiliza la contrabarra o backslash \, véase el ejemplo:
>>> print('C:\\nombre\\de\\un\\directorio')
C:\nombre\de\un\directorio
>>> print('Hola'*3)
HolaHolaHola
>>> print('Hola' +
' mundo')
Holamundo
>>> print('Hola' + ' ' + 'mundo')
Hola mundo
9
Para trabajar más cómodamente y poder estudiar algunas de las opciones que
permite realizar Python con las cadenas de texto o strings usaremos variables.
También existen los índices negativos, por ejemplo para acceder al último
carácter de la cadena sin que sea necesario conocer la posición del mismo:
>>> nombre[-1] #
Último caracter de nombre
'r'
>>> nombre[-2] # Penúltimo caracter de nombre
'e'
>>> nombre[-3] # Antepenúltimo caracter de nombre
'i'
>>> nombre[0:3]
'Jav'
10
Como se puede ver, el último caracter indicado, en el ejemplo anterior el 3,
nunca se incluye. Pero si queremos incluir el último caracter bastaría con no
indicar nada, en este otro ejemplo se puede ver cómo extraer los carácteres
desde el tercero hasta el último:
>>> nombre[2:]
'vier'
>>> nombre[:-1]
'Javie'
>>> nombre[:-2]
'Javi'
>>> nombre[21:]
''
En este caso no muestra nada, pues no existe nada desde la posición 21 del
índice hasta un final. Pero y ¿si indicamos que nos muestre desde el inicio de
la cadena hasta la posición 21?
>>> nombre[:21]
'Javier'
En este otro caso mostrará toda la cadena, pues a pesar de que el índice no
tiene 21 posiciones muestra todas las disponibles.
Ahora que ya se conoce el funcionamiento de las cadenas de carácteres y el
acceso a cada elemento de la cadena mediante índices, es posible que el
alumno se pregunte si es posible modificar el valor de algún elemento de la
cadena de carácteres. Para el tipo de dato string o cadena de carácteres no es
posible hacerlo directamente, véase un ejemplo y el error que devuelve:
11
Como se puede ver, el error es claro, indica que para el tipo de dato str (string)
no es posible la asignación de valores a un item o elemento de la cadena. Pero
si queremos modificar solo el primer carácter de la cadena ' J' por el
carácter ' X' es posible mediante la siguiente operación:
La función len() no está disponible para todos los tipos de datos, más
adelante veremos en qué otras ocasiones se puede utilizar.
Listas
Las listas son un tipo de datos en Python que permite agrupar diferentes
elementos o items, e incluso siendo estos de diferentes tipos de datos, como
carácteres, números enteros y flotantes, cadenas de caracteres, e incluso
otras listas. Se definen entre corchetes "[]" y sus elementos han de ir
separados por una coma.
Ejemplo de una lista en la que todos sus elementos son números enteros:
12
>>> caracteres
['a', 'b', 'c', 'd', 'Hola']
En este ejemplo se muestra una lista con varios tipos de datos diferentes:
E incluso se puede hacer una lista en la que sus elementos sean también
listas, esto se conoce como listas anidadas:
>>> lista_de_listas = [[3, -9], ['a', '
Hola'], [0.25, 3.14], [3, 'b', 15.9]]
>>> lista_de_listas
[[3, -9], ['a', 'Hola'], [0.25, 3.14], [3, 'b', 15.9]]
>>> letras[:-1]
['a', 'b', 'c', 'd']
>>> letras[:-2]
['a', 'b', 'c']
>>> letras[2:]
['c', 'd', 'e']
O los elementos que van desde el segundo hasta el cuarto (el elemento
indicado en el final del slice no se muestra):
>>> letras[1:4]
['b', 'c', 'd']
13
que no era posible modificar un elemento de la cadena de carácteres mediante
la reasignación de valor a su índice. En el caso de las listas si es posible, por
ejemplo:
Todos los elementos de una lista tienen un orden, al que se puede acceder
mediante índices tal y como ya hemos visto. Para añadir nuevos elementos a
la lista existe un método append() que nos permite incorporar nuevos
elementos al final de la lista, por ejemplo:
En caso de que queramos borrar todos los elemento de la lista basta con
asignar el valor de la lista con una lista vacía:
14
>>> letras = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del letras[2]
>>> letras
['a', 'b', 'd', 'e', 'f']
Volvamos a las listas anidadas, es decir, aquellas listas que sus elementos son
a su vez otras listas. Veamos cómo se pueden construir y cómo se puede
acceder a sus datos mediante múltiples índices, por ejemplo:
>>> lista_anidada[0]
[0, 1, 2]
>>> lista_anidada[0][0]
0
>>> lista_anidada[1][0]
3
A continuación veremos algunos de los métodos que se pueden utilizar con las
listas. Por ejemplo, el método len() devolverá el tamaño de la lista, es decir, el
número de elementos que tiene, véase el ejemplo:
15
>>> lista = ['Dennis', '
Ken', 'Richard']
>>> len(lista)
3
>>> mascotas.pop()
'hurón'
>>> mascotas
['perro', 'gato', 'canario', 'iguana', 'gato']
>>> mascotas.pop(1)
'gato'
>>> mascotas
['perro', 'canario', 'iguana', ' gato']
16
>>> mascotas.reverse()
>>> mascotas
['gato', 'iguana', 'canario', 'perro']
>>> mascotas.sort()
>>> mascotas
['canario', 'gato', 'iguana', ' perro']
>>> numeros = [23, 48, 5 , 19, 7 1, 9]
>>> numeros.sort()
>>> números
[5, 9, 19, 23, 48, 71]
Y cómo no, podemos realizar iteraciones con los elementos de una lista, por
ejemplo:
17
perro
gato
pez
iguana
hurón
Operadores y expresiones
Operadores relacionales
El operador "mayor que" se escribe con el símbolo > y sirve para comprobar si
el primer elemento es mayor que el segundo, por ejemplo:
Existe otro operador "mayor o igual que", se escribe >= y sirve para comprobar
si el primer elemento es mayor o igual que el segundo, por ejemplo:
18
El operador "menor que" se escribe con el símbolo < y sirve para comprobar si
el primer elemento es menor que el segundo, por ejemplo:
Por último hay también un operador "menor o igual que", se escribe <= y sirve
para comprobar si el primer elemento es menor o igual que el segundo, por
ejemplo:
Una vez explicados los operadores relacionales se pueden utilizar para realizar
algunas comprobaciones más complejas, por ejemplo, se puede comparar si
el resultado de una operación aritmética es igual a un número concreto:
>>> 2 +
3 = = 7
False
>>> 7 - 2 = = 5
True
En este caso devuelve un False por que evidentemente no son iguales, tienen
elementos diferentes, pero ¿y si comprobamos la igualdad entre el último
elemento de la primera lista y el primero de la segunda lista?
19
Operadores lógicos
No solo podremos hacer operaciones lógicas con los valores True y False,
también podremos hacerlo con expresiones algebraicas o comparaciones que
terminarán devolviendo un valor booleano igualmente, por ejemplo:
>>> 3 +
3 > 2
True
>>> 3 + 3 > 2 7
False
En este caso el resultado es False ya que en una conjunción and para que el
resultado sea True ambos predicados deben ser verdaderos, y en este ejemplo
20
la cadena 'Hola' no es igual a la cadena 'hola', ya que una comienza por
mayúscula y la otra por minúscula.
En el caso de la disyunción or basta con que uno de los dos predicados sea
verdadero para que el resultado sea True, véase el siguiente ejemplo:
En este ejemplo 'Hola' sigue sin ser igual a la cadena 'hola', pero al menos el
segundo predicado es verdadero, pues el carácter 'b' no es igual que el
carácter ' f', por lo tanto se obtiene un resultado True.
Además de los operadores and y or también existe el operador not. Es una
negación lógica o un inversor, es decir, a un valor booleano (True o False) lo
convierte en el contrario, véase el siguiente ejemplo:
>>> not T
rue
False
>>> not F alse
True
Expresiones anidadas
Podemos combinar los diferentes operadores que ya hemos visto, tales como
el operador suma +, resta -, multiplicación *, división /, módulo % o
potencias * * y podemos combinarlos con los demás operadores relacionales
como < , > , < =, > =, = = o ! =, los operadores lógicos como las conjunciones and y
disyunciones o r, e incluso podemos englobar algunas de ellas entre
paréntesis ( ), pero para realizar las operaciones correctamente es importante
conocer las normas de precedencia en estos casos de expresiones
combinadas o anidadas. Véase el siguiente ejemplo en el que se mezclan
diferentes expresiones:
3
>>> a =
>>> b = 5
>>> a * b - 2**b >= 20 and not (a % b) != 0
False
21
En todo caso, las normas de precedencia a la hora de realizar operaciones
combinadas o anidadas son las siguientes.
1. Las operaciones que se encuentren entre paréntesis, sean del tipo que
sean, aritméticas, relacionales o lógicas.
2. Después los exponentes y raíces, siempre de izquierda a derecha.
3. A continuación las multiplicaciones, divisiones y módulo, siempre de
izquierda a derecha.
4. Luego las sumas y restas, siempre de izquierda a derecha.
5. Después se han de realizar las operaciones relacionales, siempre de
izquierda a derecha.
6. Finalmente las operaciones lógicas, donde el operador n ot o inversor
tiene preferencia ante la conjunción a nd y la disyunción o r, el resto
siempre se ha de calcular de izquierda a derecha.
>>> (a % b)
3
El resultado es 3 porque es el resto de dividir 3 entre 5. Vamos a ir actualizando
la expresión con los cálculos según los vayamos resolviendo, de momento se
queda así:
Continuamos, ahora vendrían los exponentes y las raíces. En este caso no hay
raíces, pero si un exponente 2**b, que es lo mismo que 2 elevado a 5:
>>> 2**5
32
Actualizamos la expresión:
>>> a * b
22
15
Actualizamos la expresión:
Sigamos con las sumas y restas. En este ejemplo no hay sumas, pero si la
resta 15 - 32:
Actualizamos la expresión:
El resultado es True puesto que el número 3 es diferente del número 0.
Actualizamos la expresión:
Ahora nos queda una expresión únicamente lógica. Si seguimos las normas de
precedencia debemos tener en cuenta primero el operador not, en este caso la
operación es n ot True:
Actualizamos la expresión:
23
Finalmente calculamos esta operación lógica en la que hay una conjunción and:
Operadores de asignación
>>> a = 0
De esta manera la variable a tendría asignado desde este momento el valor
entero 3.
>>> a += 1
>>> print(a)
1
>>> a += 1
>>> print(a)
2
>>> a += 1
>>> print(a)
3
También podemos hacer una resta en asignación, se escribe -= y funciona
exactamente igual que el anterior solo que cada vez se resta la cantidad que
tiene a su derecha, véase el ejemplo:
24
En este caso, en vez de aumentar o disminuir el valor de uno en uno lo hemos
hecho de cinco en cinco.
>>> c = 3
>>> c *= 2
>>> print(c)
6
>>> c *= 2
>>> print(c)
12
>>> c *= 2
>>> print(c)
24
25
El primer resultado es 1, ya que es el resto de dividir 29 entre 2, así pues, el
nuevo valor de la variable e pasa a ser 1. En el segundo cálculo el resultado
vuelve a ser 1, ya que es el resto de dividir 1 entre 2.
>>> f = 2
>>> f **= 2
>>> print(f)
4
>>> f **= 2
>>> print(f)
16
>>> f **= 2
>>> print(f)
256
Control de flujo
El flujo de un programa es la sucesión de acciones que se irán ejecutando en
un programa. Veremos los diferentes controles de flujo que tenemos
disponibles en Python como el control de flujo condicional en el que se podrá
elegir la ejecución de una acción u otra, o el control de flujo iterativo, que
repetirá un bloque de instrucciones hasta que se cumpla una condición
específica.
Sentencia if
if True:
print('Hello world!')
26
al evaluar la expresión condicional if resulta True, pero como hemos puesto
una expresión lógica True sin más siempre será verdadero, por lo tanto se
imprimirá siempre el mensaje.
Veamos otro ejemplo de condicional if con otra expresión diferente:
En este caso la variable saludo, que tiene una longitud de 4, no cumple la
condición de que su longitud sea mayor o igual a 7, por lo tanto no se
imprimirá ningún mensaje.
>>> a = 5
>>> b = 17
>>> if a < b and a * 4 > b:
... print('Esta expresión es verdadera.')
...
Esta sentencias es verdadera.
>>> a = 0
>>> b = 5
>>> if a > b or 25 % b == 0
:
... print('Esta expresión es verdadera.')
...
27
Esta expresión es verdadera.
>>> a = 5
>>> b = 10
>>> if a == 5:
... print('a vale', a)
... if b == 10:
... print('
b vale', b)
...
a vale 5
b vale 10
Hasta ahora hemos visto cómo hacer que se ejecute un bloque de código
cuando se cumple una condición, pero también podemos ejecutar código
cuando la condición no se cumple. Para ello está la palabra reservada else y
se utiliza de la siguiente manera:
28
...
Saliendo del sistema ...
En realidad el archivo puede tener cualquier otro nombre, este solo es uno de
ejemplo. Para ejecutar un programa Python contenido en un archivo se ha de
hacer de la siguiente forma desde la consola:
python3 mi_primer_programa.py
Saliendo del sistema ...
29
print('Bien')
elif nota >= 5:
print('Suficiente')
else:
print('Insuficiente')
python3 calcular_nota.py
Introduce la nota: 10
Sobresaliente
python3 calcular_nota.py
Introduce la nota: 7.5
Notable
python3 calcular_nota.py
Introduce la nota: 6.9
Bien
python3 calcular_nota.py
Introduce la nota: 5
Suficiente
python3 calcular_nota.py
Introduce la nota: 4.9
Insuficiente
Sentencia while
c = 0
30
c += 1
print('c vale', c)
En este simple programa comenzamos por iniciar una variable c con valor 0. A
continuación iniciamos un bucle while que ejecutará el código que contiene en
su cuerpo solo si se cumple la condición en la que se evaluará si la
variable c en ese momento tiene un valor menor o igual que 5. La primera vez
la variable c vale 0 , por lo que esta expresión devolverá True, así que el
bucle w hile ejecutará el código con contiene. Lo primero que hace es
incrementar en 1 el valor actual de c , por lo que en este momento, tras ejecutar
esta línea c pasaría a valer 1 . A continuación imprime un mensaje que dice "c
vale 1" y entonces es cuando vuelve a evaluar de nuevo la expresión inicial,
solo que ahora c ya no vale 0 , ha pasado a valer 1 .
En esta segunda iteración se vuelve a cumplir que c sea menor o igual que 5,
por lo que repetirá el código que hay dentro del bucle while. Una vez más
incrementará en 1 el valor de c, por lo que a partir de ese momento la
variable c pasa a valer 2. Después imprime el mensaje "c vale 2" y vuelve de
nuevo a evaluar la expresión inicial.
Este proceso se repetirá varias veces, hasta que en una de las iteraciones el
valor de c se incremente hasta valer 6, en cuyo caso, al volver a evaluar la
expresión inicial devolverá un False, pues 6 no es menor o igual que 5. En ese
momento el bucle ya no ejecutará más veces el código que contiene. A
continuación un ejemplo de cómo sería la ejecución de nuestro programa con
este bucle w hile que hemos escrito.
python3 bucle_while.py
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
Una particularidad que tiene el bucle while en Python es que puede usar la
palabra reservada else para ejecutar una acción tras finalizar el bucle, véase el
siguiente ejemplo:
c = 0
31
Si ejecutamos de nuevo nuestro programa con esta inclusión de la palabra
reservada else veremos que el resultado es el mismo y además añade el
mensaje "Saliendo del bucle" al finalizar este:
python3 bucle_while.py
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
Saliendo del bucle
c = 0
python3 bucle_while.py
c vale 1
c vale 2
Se corta la iteración cuando c vale 3
32
iteración en la por ejemplo que se cumpla una condición. Vamos a modificar el
código de nuestro programa de la siguiente manera:
c = 0
python3 bucle_while.py
c vale 1
c vale 2
c vale 4
c vale 5
c vale 6
Saliendo del bucle
while True:
print('Selecciona una opción:\n')
print('1: Saludar')
print('2: Sumar dos números')
print('3: Salir\n')
user_choice = i
nput()
33
n1 = float(input('Introduce el primer número: '))
n2 = float(input('Introduce el segundo número: '))
print(' El resultado de la suma es:', n1 + n2)
elif user_choice == '3':
print(' Saliendo del programa!')
break
else:
print(' Opción no válida, vuelve a intentarlo.\n')
Sentencia for
La sentencia for nos permite realizar iteraciones de una manera muy similar a
la sentencia while, solo que reduce considerablemente la complejidad, puesto
que no es necesario crear variables previamente con índices ni necesitaremos
hacer incrementos con operadores de incremento.
Normalmente se utiliza para iterar sobre listas o elementos compuestos en los
que puede ser necesario recorrer cada uno de esos elementos. Veamos un
ejemplo muy simple, crearemos un archivo nuevo llamado b ucle_for.py y en él
escribiremos el siguiente código:
En este simple programa creamos primero una variable llamada numeros de tipo
lista, y como valor le asignamos una lista de números enteros del 1 al 5. A
continuación usamos la palabra reservada f or, luego escribimos el nombre de
la variable temporal que usaremos dentro del bucle para referirnos a cada
elemento por el que iteraremos, en este caso n umero, luego la palabra
reservada i n y finalmente el nombre de la variable que contiene la información
por la que realizaremos las iteraciones, en este caso nuestra lista n umeros. A
continuación vendría el cuerpo del bucle, siempre identado con una
tabulación, en este caso imprimir el valor de la variable temporal n umero que en
cada iteración tendrá por valor cada uno de los elementos de la lista n umeros.
Veamos un ejemplo de ejecución de este pequeño programa:
python3 bucle_for.py
1
2
3
4
5
34
como por ejemplo numeros[0] o numeros[1], ni tampoco ha hecho falta crear una
variable a modo de contador para usarla como índice, simplemente itera de
uno en uno a través de todos los elementos, desde el primero hasta el último.
Si quisiéramos tener un control de cada elemento mediante un índice se puede
realizar de varias maneras, pero Python dispone de un método
llamado e numerate() que podremos utilizar para facilitar el proceso. Vamos a
reemplazar el código de nuestro archivo b ucle_for.py con el siguiente código
de ejemplo:
python3 bucle_for.py
1 1
2 2
3 3
4 4
5 5
print(numeros[indice])
print(numeros)
Hemos incluido al final del programa una línea que imprima todos los
elementos de la lista, fuera del bucle for, pero comprobar que el cambio de
35
valor en el tercer elemento prevalece en el valor de la lista. Si ejecutamos el
programa de nuevo veremos el siguiente resultado:
python3 bucle_for.py
1
2
9
4
5
[1, 2, 9, 4, 5]
El tercer elemento que antes era 3 ahora es su propio valor multiplicado por 3,
es decir, 9.
La sentencia for no solo sirve para recorrer los elementos de una lista, también
tiene otras utilidades interesantes como por ejemplo la de recorrer todos los
carácteres de una variable de tipo string o cadena de texto, por ejemplo,
modifiquemos el código de nuestro programa en el archivo b ucle_for.py con el
siguiente código:
Primero hemos creado una variable de tipo string con el mensaje "Hola qué
tal" y luego hemos creado un bucle for en el que hemos creado la
variable caracter para iterar por cada carácter de la variable cadena. Si
ejecutamos el programa se imprimirán todos los caracteres uno a uno, incluido
los espacios, que también son un carácter:
python3 bucle_for.py
H
o
l
a
q
u
é
t
a
l
36
cadena = 'Hola qué tal'
n enumerate(cadena):
for i, c i
print(cadena[i], c)
python3 bucle_for.py
H H
o o
l l
a a
q q
u u
é é
t t
a a
l l
Como podemos ver, funciona igual que cuando usábamos el bucle for en el
ejemplo anterior. Probemos a modificar el primer carácter "H" por "X" y veamos
qué sucede en este caso:
n enumerate(cadena):
for i, c i
if i == 0:
cadena[i] = 'X'
print(cadena[i], c)
print(cadena)
python3 bucle_for.py
Traceback (most recent call last):
File "bucle_for.py", line 5, i
n <
module>
cadena[i] = 'X'
TypeError: 'str' object does not support item assignment
Esto sucede porque las variables de tipo string son inmutables, así que en
esta ocasión no podremos modificar el valor de ningún caracter.
En un bucle for también podemos utilizar la función range(), que generará una
lista entre un rango numérico, por ejemplo, si queremos imprimir los 10
37
primeros números utilizando la función range() tendríamos que editar el
archivo bucle_for.py con el siguiente código:
python3 bucle_for.py
0
1
2
3
4
5
6
7
8
9
La función range() permite introducir dos valores separados por coma entre
sus paréntesis, por ejemplo si queremos obtener un rango numérico entre el
número 2 7 y el número 3 1 tendríamos que escribir range(27, 31), modifiquemos
nuestro código para verlo:
python3 bucle_for.py
27
28
29
30
Colecciones
En Python, además de las listas también tenemos un tipo especial de dato
llamado colecciones, y a que a su vez se divide en varias estructuras de datos
que veremos en detalle en esta sección:
● Tuplas
● Conjuntos
● Diccionarios
38
Además, podemos simular dos colecciones tradicionales de otros lenguajes
como las Pilas y las Colas utilizando datos de tipo lista o librerías y módulos
de Python.
Tuplas
Las tuplas son unas colecciones parecidas a las listas, con la diferencia de que
las tuplas son inmutables. Se suelen utilizar para asegurarnos de que
determinados datos no se puedan modificar. Python utiliza tuplas en alguna de
sus funciones para devolver resultados inmutables. La manera de definir un
dato de tipo tupla es parecido a las listas, solo que en vez de usar
corchetes [ ] se utilizan paréntesis ( ). Dentro de los paréntesis se incluyen los
elementos como si fueran una lista. Para poder ver un ejemplo práctico vamos
a crear un archivo llamado t upla.py y dentro vamos a incluir el siguiente
código:
print(tupla)
Las tuplas, al igual que las listas, aceptan indexación y slicing. Por ejemplo,
podremos consultar el primer elemento con el índice [0] o el último elemento
de la tupla con el índice [-1]:
print(tupla[0])
print(tupla[-1])
python3 tupla.py
100
3.14
python3 tupla.py
39
([1, 2, 3], 3.14)
En este caso, uno de los elementos de la tupla es una lista, también podremos
acceder a uno de los elementos internos de esa lista tal y como lo hacíamos
cuando teníamos listas dentro de listas, con dobles índices, por ejemplo:
python3 tupla.py
2
Antes se ha mencionado que las tuplas es un tipo de dato parecido a las listas
pero que su valor es inmutable. Vamos a modificar el código de nuestro
programa para intentar modificar el primer valor de nuestra tupla y ver el error
que devuelve:
print(tupla)
python3 tupla.py
Traceback (most recent call last):
File "tupla.py", line 3, in <module>
tupla[0] = 37
TypeError: 'tuple' object does not support item assignment
Al igual que la listas también tienen una longitud, que representaría el número
de elementos que tiene, y para ello se utiliza de nuevo la función len(), por
ejemplo:
print(len(tupla))
python3 tupla.py
4
40
modifiquemos el código de nuestro programa para que nos devuelva la
posición del elemento [1, 2, 3]:
python3 tupla.py
2
Otro método interesante que podemos usar tanto en listas como en tuplas en
el método .count(), este nos devolverá el número de veces que aparece
repetido un elemento conocido en la lista o en la tupla. Editemos el código de
nuestro programa para ver unos ejemplos:
python3 tupla.py
3
Aunque las tuplas y las listas tienen muchos métodos en común hay algunos
métodos que no están disponibles en las tuplas como por ejemplo el
método . append() para añadir nuevos elementos, puesto que las tuplas son
inmutables y no se pueden ni editar, si siquiera añadir o eliminar elementos.
Conjuntos
Para empezar a trabajar con los conjuntos vamos a crear un archivo nuevo
llamado conjuntos.py y vamos a ir escribiendo código en él. Para crear un
conjunto, si este va a ser un conjunto vacío se hace de la siguiente manera:
Pero si queremos un conjunto que contenga elementos solo hay que escribir
los elementos que se quieren añadir a dicho conjunto entre llaves {} y
separados por comas, por ejemplo:
41
print(conjunto)
python3 conjunto.py
{1, 2, 3}
Ahora vamos a utilizar el método .add() para añadir un nuevo elemento a este
conjunto, por ejemplo el número 4:
conjunto.add(4)
python3 conjunto.py
{1, 2, 3, 4}
Se puede ver cómo se ha añadido el número 4 al conjunto, justo al final. Ahora
vamos a añadir otro elemento nuevo, por ejemplo el número 0 y mostremos
cómo imprime el valor del conjunto:
conjunto.add(0)
python3 conjunto.py
{0, 1, 2, 3, 4}
conjunto.add('H')
python3 conjunto.py
{0, 1, 2, 3, 4, 'H'}
La letra "H" la ha añadido al final. Ahora vamos a añadir dos letras más, la letra
"A" y la letra "Z" y veamos qué pasa:
conjunto.add('A')
conjunto.add('Z')
python3 conjunto.py
{0, 1, 2, 3, 4, 'A', 'Z', 'H'}
Los números los sigue mostrando en orden numérico de menor a mayor, pero
los carácteres los muestra en un orden aleatorio, de hecho, si ejecutamos el
programa varias veces seguidas podremos comprobar que el orden de las
letras "A", "H" y "Z" va cambiando, por eso decimos que los conjuntos son un
tipo de colecciones desordenadas.
42
Los conjuntos son muy útiles para comprobar la pertenencia a un grupo, por
ejemplo, hagamos un grupo de personas llamado conjuntoA:
python3 conjunto.py
True
print(conjuntoA)
python3 conjunto.py
{'Alice', 'Javier', 'Bob'}
43
hagámoslo por pasos, primero creamos la variable lista de tipo lista con
elementos repetidos:
print(conjunto)
python3 conjunto.py
{1, 2, 3}
print(lista)
python3 conjunto.py
[1, 2, 3]
En vez de hacer este cast doble en varias líneas podemos hacerlo más
sencillo en una sola línea de la siguiente manera:
print(lista)
python3 conjunto.py
[1, 2, 3]
print(set(cadena))
44
python3 conjunto.py {'S', 'u', 'b', 'E', 'r', 'd', 'n', 'o', 'q', 'i', 'e', '
', 't', 'a', 'p', 'R', '
l'}
Como resultado se crea un conjunto con todas las letras que aparecen en la
variable de tipo string cadena, pero sin repetir y sin guardar un orden
específico.
Diccionarios
El último tipo de colección que vamos a ver son los diccionarios. Junto a las
listas son las colecciones más utilizadas en Python. Se basa en una estructura
mapeada, del inglés mapping donde cada elemento de la colección se
encuentra identificado mediante una clave única, por lo tanto no puede haber
dos claves iguales en el mismo diccionario. Así pues, para crear un diccionario
deberemos indicar siempre una clave, que generalmente será una cadena de
caracteres, y un valor para cada elemento. Crearemos un nuevo archivo
llamado d iccionario.py para ir añadiendo el código de esta sección. Podemos
crear un diccionario vacío escribiendo unas simples llaves { } sin nada dentro:
diccionario_vacio = {}
print(diccionario_vacio)
python3 diccionario.py
{}
diccionario_vacio = {}
print(type(diccionario_vacio))
python3 diccionario.py
<class 'dict'>
python3 diccionario.py
{'amarillo': 'yellow', '
azul': '
blue', 'verde': 'green'}
45
Con esto ya tendríamos un diccionario definido con tres elementos con la
estructura "clave:valor". Si quisiéramos saber el valor que tiene el
color amarillo en esta estructura de datos tendríamos que hacerlo de la
siguiente manera:
print(diccionario['amarillo'])
python3 diccionario.py
yellow
También podemos utilizar números como claves o índices, por ejemplo, vamos
a crear un diccionario nuevo llamado números en el que las claves serán
números enteros y los valores un string con su nombre:
print(numeros[57])
python3 diccionario.py
cincuenta y siete
print(edades)
python3 diccionario.py
{'Javier': 18, 'Alice': 21, 'Bob': 33}
Del mismo modo que podíamos modificar los registros de una lista también
podremos modificar los registros de un diccionario. Volvamos al ejemplo de
los colores para ver un ejemplo. Vamos a crear un diccionario llamado colores,
este va a contener los nombres de algunos colores como clave y su traducción
al inglés como valor:
46
colores = {'amarillo': '
yellow', 'azul': 'blue', 'verde': 'green'}
print(colores)
print(colores)
python3 diccionario.py
{'amarillo': 'yellow', '
azul': ' blue', 'verde': 'green'}
{'amarillo': 'yellow', ' azul': ' purple', 'verde': 'green'}
También podemos borrar una entrada del diccionario, es decir una clave y su
valor, para ello es necesario usar el método .del() y especificar la clave a
borrar, véase el ejemplo:
del(colores['azul'])
print(colores)
python3 diccionario.py
{'amarillo': 'yellow', '
verde': 'green'}
Otra cosa bastante útil que podemos hacer con un diccionario es recorrer
todos sus ítems con un bucle for, veamos un primer ejemplo:
python3 diccionario.py
Javier
Alice
Bob
De este modo podremos listar la clave de cada uno de los índices del
diccionario, pero quizás podría interesarnos acceder a los valores. Esto se
puede realizar de la siguiente manera:
47
python3 diccionario.py
18
21
33
python3 diccionario.py
Javier 18
Alice 21
Bob 33
Sin embargo esta forma es un poco rudimentaria, existen formas más óptimas
de acceder a ambos datos, por ejemplo con el método .items() y seteando en
el bucle las dos variables de la siguiente manera:
python3 diccionario.py
Javier 18
Alice 21
Bob 33
Para terminar esta sección veremos cómo se puede crear una estructura de
datos un poco más avanzada, por ejemplo creando una lista con varios
diccionarios como elementos y cómo podemos luego acceder a los datos de
cada diccionario con un bucle for, bien para mostrarlos o para editarlo,
veamos un ejemplo. Primero crearemos una lista vacía llamada alumnos:
alumnos = []
alumnos.append(a)
48
Repetimos los dos pasos anteriores un par de veces con otros dos alumnos
más:
python3 diccionario.py
Javier 1 A
Alice 2 C
Bob 3 B
Como se puede ver, es relativamente sencillo crear una lista que haga la
función de una simple base de datos de alumnos, y ahora ya sabemos cómo
podemos acceder a los datos de cada elemento de la lista, que son los datos
de los alumnos.
Pilas
El lenguaje Python no implementa una colección del tipo pila como en otros
lenguajes, sin embargo podemos simularlas fácilmente con listas. Una pila es
una colección de elementos ordenados y únicamente permite dos acciones,
añadir elementos a la pila y sacar elementos de la pila. Lo interesante de las
pilas es que el último elemento en entrar en la pila es el primero en salir, en
inglés se denomina L IFO (Last In, Fist Out), como si se tratara de una pila de
platos sucios que hay que lavar a mano, se van dejando encima de una mesa,
y luego se van cogiendo uno a uno para lavarlos, en cuyo caso se coge
primero el último plato sucio que se dejó en la pila.
Para simular el comportamiento de una p ila en Python comenzaremos
creando un nuevo fichero llamado p ila.py y crearemos una lista
llamada p ila con unos cuantos valores:
49
A continuación añadimos un nuevo elemento a pila mediante el
método .append(), que es el que usábamos para añadir elementos a una lista,
ya que es exactamente lo que queremos hacer, puesto que estamos usando
listas para simular una pila. Añadamos también una línea para imprimir el
resultado tras añadir un elemento a la pila:
pila.append(5)
print(pila)
pila.pop()
print(pila)
python3 pila.py
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4]
pila.pop()
pila.pop()
pila.pop()
print(pila)
python3 pila.py
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4]
[0, 1]
Colas
Una cola es una estructura de datos parecida a las pilas, a diferencia de que el
primer elemento en entrar en la cola es el primero en salir, en inglés se
50
denomina FIFO (First In, First Out). Podríamos compararlo con una cola para
entrar en la cita con el médico, el que primero llega es atendido y cuando
termina sale.
Para poder ver cómo implementar una cola en Python primero vamos a crear
un nuevo archivo llamado cola.py y tendremos que importar el
módulo deque de la librería estándar de Python collections de la siguiente
manera:
print(cola)
python3 cola.py
deque([])
Ahora vamos a añadir un primer elemento a esta cola que inicialmente está
vacía, como lo que contiene dentro es una lista podemos usar el
método .append() que ya habíamos usado anteriormente para añadir
elementos a una lista, por ejemplo una cadena de carácteres, además
imprimimos el contenido de la cola de nuevo para comprobar que la cola ya
tiene al menos un elemento:
cola.append('Javier')
print(cola)
python3 cola.py
deque([])
deque(['Javier'])
cola.append('Bob')
cola.append('Alice')
print(cola)
51
python3 cola.py
deque([])
deque(['Javier'])
deque(['Javier', 'Bob', 'Alice'])
Ahora que ya tenemos una cola con varios elementos, que han sido
introducidos secuencialmente, vamos a usar un método propio del
módulo deque que hemos importado llamado .popleft() para eliminar el
elemento de la c ola que entró primero, en este caso es la cadena 'Javier', e
imprimamos el nuevo estado de la cola:
cola.popleft()
print(cola)
python3 cola.py
deque([])
deque(['Javier'])
deque(['Javier', 'Bob', 'Alice'])
deque(['Bob', 'Alice'])
Para trabajar la entrada de datos por teclado vamos a crear un nuevo archivo
llamado entrada_por_teclado.py y vamos a ir añadiendo código para su estudio.
Por ejemplo, vamos a hacer un pequeño programa que pida al usuario
introducir un número decimal (float), y para ello vamos a usar en primera
instancia la función i nput(). Una vez que el usuario introduzca un número
decimal éste se imprimirá por pantalla:
52
decimal = input('Introduce un número decimal: ')
print(decimal)
python3 entrada_por_teclado.py
Introduce un número decimal: 3.14
3.14
print(type(decimal))
python3 entrada_por_teclado.py
Introduce un número decimal: 3.14
3.14 <class 'str'>
Quizás nos interese que cuando el usuario introduzca el número decimal este
se convierta en un tipo float, para ello tendremos que indicarlo usando la
función f loat() y englobando el input() dentro:
python3 entrada_por_teclado.py
Introduce un número decimal: 3.14
3.14 <class 'float'>
Otra manera de pedir datos por teclado varias veces seguidas puede ser
usando un bucle for que realice 3 iteraciones. Por ejemplo, creamos una
variable v alores de tipo lista vacía:
valores = []
for x in r
ange(3):
valores.append(input('Introduce un valor cualquiera: '))
print(valores)
53
python3 entrada_por_teclado.py
Introduce un valor cualquiera: abcd
Introduce un valor cualquiera: Hola
Introduce un valor cualquiera: 1234
['abcd', 'Hola', '1234']
Como se puede ver, hemos introducido varios valores dentro de una lista,
hemos completado una colección introduciendo datos por teclado varias
veces.
import sys
print(sys.argv)
54
Nótese que el propio nombre del programa es en sí un argumento, el primer
argumento de la lista, el que está en la posición 0. Todos los demás
argumentos se han convertido a formato string automáticamente y cada uno
está en una posición del índice de la lista, respetando el mismo orden que se
empleó a la hora de escribirlos al ejecutar el programa.
Ahora vamos a cambiar el código de nuestro programa para que imprima una
frase un número de veces. Haremos que la frase a imprimir sea el primer
argumento (entre comillas simples o dobles) y el número de veces que se
imprimirá será el segundo argumento:
import sys
for r in r
ange(repeticiones):
print(texto)
import sys
for r in r
ange(repeticiones):
print(texto)
else:
print('Error, introduce los argumentos correctamente.')
print('Ejemplo: ' +
sys.argv[0] + ' \'Texto cualquiera\' 3')
55
argumentos, contando como el nombre del archivo del propio programa como
primer argumento.
Salida de datos
'
a = me llamo Javier'
b = 3
python salida_por_pantalla.py
Hola me llamo Javier, mi número favorito es el 3
Esta es una manera muy simple de imprimir cadenas de texto mezcladas con
variables de diferentes tipos, pero si queremos tener un mayor control sobre
las variables que queremos imprimir debemos comenzar a usar un formato de
escritura de las cadenas de carácteres, en Python se hace con el
método . format(), por ejemplo:
'
a = me llamo Javier'
b = 3
python salida_por_pantalla.py
Hola me llamo Javier, mi número favorito es 3
'
a = me llamo Javier'
b = 3 print('Hola {0}, mi número favorito es {1}'.format(a, b))
56
python salida_por_pantalla.py
Hola me llamo Javier, mi número favorito es 3
Hola 3, mi número favorito es me llamo Javier
'
a = me llamo Javier'
b = 3
python salida_por_pantalla.py
Hola me llamo Javier, mi número favorito es 3 y tengo 3 mascotas.
'
a = me llamo Javier'
b = 3
python salida_por_pantalla.py
Hola me llamo Javier, mi número favorito es 3.
python3 salida_por_pantalla.py
57
Hola
python3 salida_por_pantalla.py
Hola
python3 salida_por_pantalla.py
Hola
Aquí se pueden apreciar los 8 espacios que hay por la izquierda, luego los 4
carácteres que tiene la palabra Hola y después los otros 8 espacios restantes,
en total suman 20 carácteres.
python3 salida_por_pantalla.py
Hol
58
python3 salida_por_pantalla.py
Hol
print('{:4d}'.format(10))
print('{:4d}'.format(100))
print('{:4d}'.format(1000))
python3 salida_por_pantalla.py
10
100
1000
print('{:04d}'.format(10))
print('{:04d}'.format(100))
print('{:04d}'.format(1000))
python3 salida_por_pantalla.py
0010
0100
1000
print('{:.2f}'.format(3.141592653))
python3 salida_por_pantalla.py
3.14
59
Ahora vamos a añadir otro número decimal, por ejemplo 153.21, y vamos a
alinear tanto la parte entera, como la coma y tres decimales:
print('{:7.3f}'.format(3.141592653))
print('{:7.3f}'.format(153.21))
python3 salida_por_pantalla.py
3.142
153.210
Al querer imprimir tres decimales debemos contar esos tres carácteres más el
carácter del punto (.) y los tres carácteres que representan la parte entera más
grande de los dos número decimales, en total hacen 7 carácteres. Así pues, en
la referencia { :7.3f} el 7 es la cantidad de carácteres que se van a mostrar,
el . 3 la cantidad de decimales, y si el número no tiene esa cantidad de
decimales por defecto rellenará con ceros por la derecha, y finalmente la
letra f para indicar que se trata de números de coma flotante (float) .
Si se quisiera rellenar con ceros por la izquierda en vez de espacios hay que
hacer lo mismo que en el ejemplo anterior, añadir en la referencia un 0 delante
del número de caracteres que ocupará la cadena:
print('{:07.3f}'.format(3.141592653))
print('{:07.3f}'.format(153.21))
python3 salida_por_pantalla.py
003.142
153.210
Funciones
Las funciones son fragmentos de código que se pueden invocar o ejecutar
varias veces gracias a un nombre único que las identifica. Estas pueden recibir
y devolver información para comunicarse con el programa principal.
Crearemos un archivo llamado f unciones.py en el que iremos añadiendo
código a medida que vayamos aprendiendo los conceptos y el funcionamiento
de las funciones en Python.
Definición de funciones
Para definir una función primero debemos utilizar la palabra reservada def, a
continuación el nombre que le queramos poner a la función, después apertura
y cierre de paréntesis sin nada en su interior () y finalmente dos puntos :. En la
60
línea siguiente debemos dejar una identación o tabulación a la izquierda y a
continuación el código que queremos que se ejecute al invocar a esta función,
por ejemplo:
def saludo():
print('Hola mundo!')
Hasta aquí ya tenemos el código de una función. Pero ahora debemos añadir
la línea de código para invocar, bastaría con escribir simplemente el nombre
de la función y los paréntesis, por ejemplo:
def saludo():
print('Hola mundo!')
saludo()
python3 funciones.py
Hola mundo!
Vamos a crear un ejemplo un poco más complicado que un simple saludo, por
ejemplo una función que muestre por pantalla la tabla de multiplicar del
número 5:
def tabla_de_multiplicar_del_5():
for i in range(10):
print(' 5 * {} = {}'.format(i, i*5))
tabla_de_multiplicar_del_5()
python3 funciones.py
5 * 0 = 0
5 * 1 = 5
5 * 2 = 10
5 * 3 = 15
5 * 4 = 20
5 * 5 = 25
5 * 6 = 30
5 * 7 = 35
61
8 = 40
5 *
5 * 9 = 45
En este caso hemos utilizado un bucle for() para iterar a través de todos los
valores comprendidos entre 0 y 9, esto lo conseguimos mediante range(10), y
cada valor del rango en la iteración lo almacenaremos en la variable
temporal i . Después se añade dentro del cuerpo del bucle for() un print() de
un mensaje que contiene dos referencias, la primera mostrará el valor de i en
ese momento y la segunda mostrará el resultado de multiplicar i por 5 .
Dentro del cuerpo de una función podremos crear nuevas variables y utilizar
cualquier sentencia de las ya vistas, e incluso llamar a otras funciones. Es muy
importante tener en cuenta que cuando se define una variable nueva dentro
del cuerpo de una función, esta variable solo estará disponible y visible dentro
del cuerpo de la función, no fuera. Veamos un ejemplo en el que
provocaremos un error al tratar de utilizar fuera de la función una variable
definida dentro de la función:
def mi_funcion():
nombre = 'Frank'
print(nombre)
python3 funciones.py
Traceback (most recent call last):
File "funciones.py", line 4, i
n <
m
odule>
print(nombre)
NameError: name 'nombre' is not defined
def mi_funcion():
print(nombre)
mi_funcion()
python3 funciones.py
Frank
62
En este caso si tenemos la variable nombre disponible dentro de la función
porque es una variable definida en un ámbito global. El único requisito que
necesita una variable global para ser utilizada en funciones es que esta
variable debe estar definida antes de la llamada a la función, si no daría un
error indicando que se está utilizando una variable que no existe.
Retorno de valores
Hemos visto que una variable dentro de una función no tiene alcance fuera de
ella. Para conectar la función con el exterior podemos devolver valores al
programa principal. Esto se realiza con la palabra reservada return en el
cuerpo de la función. Veamos un ejemplo en el que la
función m i_funcion devuelve una cadena de texto al ser ejecutada, en este
caso se ejecutará dentro de un p rint():
def mi_funcion():
return 'Una cadena de texto'
print(mi_funcion())
python3 funciones.py
Una cadena de texto
También podemos asignar a una variable el valor devuelto por la función para
poder utilizarlo varias veces en el programa principal sin necesidad de invocar
a la función cada vez que se quiera utilizar es valor, por ejemplo:
def mi_funcion():
return 'Hola mundo!'
print(saludo)
print(saludo)
print(saludo)
python3 funciones.py
Hola mundo!
Hola mundo!
Hola mundo!
63
Las funciones pueden devolver cualquier tipo de dato de los disponibles en
Python y que hemos visto al principio. Veamos un ejemplo en el que la función
devuelve un dato de tipo lista, y veamos también cómo podemos utilizar
índices y slicing con el valor devuelto.
def mi_funcion():
return [1, 2, 3, 4,
5]
python3 funciones.py
[1, 2, 3, 4, 5]
1
5
[2, 3, 4]
def mi_funcion():
return [1, 2, 3, 4,
5]
print(lista_numeros)
print(lista_numeros[0])
print(lista_numeros[-1]
)
print(lista_numeros[1:4 ])
python3 funciones.py
[1, 2, 3, 4, 5]
1
5
[2, 3, 4]
Las funciones en Python no tienen por que devolver solo un dato, pueden
devolver varios datos separados por coma, y estos no tienen porqué ser del
mismo tipo. Veamos un ejemplo en el que nuestra función devuelve tres datos
diferentes, una cadena de texto, un número y una lista:
def mi_funcion():
return 'Hola', 7
7, [1, 2
, 3]
64
resultado = mi_funcion()
print(resultado)
python3 funciones.py
('Hola', 77, [1, 2, 3])
El valor que de devuelve es una colección de tipo tupla como las que ya
hemos visto anteriormente, y como ya sabemos, las tuplas son colecciones
inalterables y que pueden contener datos de diferentes tipos.
Podemos aprovechar este retorno de varios datos (del mismo tipo o no) para
asignar cada valor devuelto a diferentes variables en una sola línea de la
siguiente manera:
def mi_funcion():
return 'Hola', 7
7, [1, 2
, 3]
python3 funciones.py
Hola
77
[1, 2, 3]
Como se puede ver, se asignará cada valor devuelto, del tipo que sea, a cada
variable, siguiendo el mismo orden separadas por comas.
Enviando valores
print(suma(1, 2))
print(suma(27, -14))
python3 suma_de_numeros.py
3
65
13
En este caso sí que nos interesa ejecutar la misma función tres veces. El
código de la función que se va a ejecutar cada vez es exactamente el mismo,
lo único que cambia son los argumentos que pasamos a la hora de llamar a la
función, a veces pasamos los valores 1 y 2, y otras veces pasamos los
valores 2 7 y - 14. No importa qué números pasemos, la función devolverá
mediante la instrucción r eturn el resultado de lo que pasemos como a más lo
que pasemos como b .
Argumentos y parámetros
Los parámetros son aquellas claves que se definen dentro de los paréntesis a
la hora de definir una función, en este caso los parámetros son a y b:
print(suma(1, 2))
print(suma(27, -14))
print(resta(b=2, a=1))
python3 resta_de_numeros.py
-1
66
A la hora de hacer uso de la función resta() le estamos diciendo que el
parámetro b va a valer 2 y el parámetro a va a valer 1. Están desordenados,
pero el resultado es el que se espera.
print(resta(2))
python3 resta_de_numeros.py
-3
print(resta(7, 3))
python3 resta_de_numeros.py
4
def doblar_valor(numero):
print(numero * 2)
67
n = 10
doblar_valor(n)
print(n)
python3 valor_y_referencia.py
20
10
def doblar_valores(numeros):
for i, n in enumerate(numeros):
numeros[i] *= 2
print(ns)
python3 valor_y_referencia.py
[20, 100, 200]
68
Como se puede ver, en este caso si se ha doblado el valor inicial de cada
elemento de la lista. Esto sucede porque tal y como se explicaba al inicio de
este punto, las colecciones, listas, diccionarios y conjuntos se pasan a las
funciones por referencia, en vez de una copia local dentro de la función se
accede a los elementos que contiene el objeto original.
python3 valor_y_referencia.py
20
def doblar_valores(numeros):
for i, n in enumerate(numeros):
numeros[i] *= 2
print(ns)
python3 valor_y_referencia.py
[10, 50, 100]
En este caso se ha pasado como referencia una copia de la lista ns no la lista
original, por lo que la función ha duplicado el valor de cada elemento solo de
en la copia, la variable ns contiene una lista intacta.
69
Argumentos indeterminados
Hasta ahora hemos visto dos maneras de pasar información a una función en
Python, una es mediante una serie de argumentos que luego debe coincidir en
número y posición con los parámetros definidos en la función, y otra manera
que hemos visto es mediante nombres en los parámetros, donde ya no
importa el orden. Pero ¿y si queremos pasar un número indeterminado de
argumentos?
def indeterminados_posicion(*args):
print(args)
indeterminados_posicion(5, '
Hola mundo!', [1, 2, 3])
indeterminados_posicion('aaa', ' bbb')
indeterminados_posicion(1, - 15, 27, 'My name is Bobby Brown', 3.14)
python3 indeterminados_posicion.py
(5, 'Hola mundo!', [1, 2, 3])
('aaa', 'bbb')
(1, -15, 27, 'My name is Bobby Brown', 3.14)
En la definición de la función hemos tenido que añadir *args (se puede llamar
como se quiera, pero es el nombre más común) como parámetro, lo que
quiere decir que se almacenará en una variable local args todos los
argumentos, sean los que sean. En cada ejecución de la
función i ndeterminados_posicion() se ha pasado una cantidad diferente de
argumentos y de diferentes tipos. El resultado en todos los casos es
una t upla con todos los elementos en el orden en el que fueron pasados.
Recordemos que las tuplas son colecciones inalterables, no se puede
modificar su contenido.
En este ejemplo los argumentos que se pasen en un orden tendrán ese mismo
orden dentro de la función como parámetros.
Pero si quisiéramos pasar un número indeterminado de parámetros mediante
una serie de nombres también podemos hacerlo. Para ello vamos a crear un
nuevo archivo llamado i ndeterminados_nombre.py y escribiremos en él el
siguiente código:
def indeterminados_nombre(*
*kwargs):
print(kwargs)
indeterminados_nombre(n=5, c
='Hola mundo!', l=[1, 2, 3])
70
python3 indeterminados_nombre.py
{'n': 5, 'c': 'Hola mundo!', 'l': [1, 2, 3]}
Funciones recursivas
Una función recursiva es una función que se puede ejecutar a sí misma varias
veces. Tienen un comportamiento muy similar a las de las sentencias iterativas
que hemos ido aprendiendo. Vamos a crear un nuevo archivo
llamado f uncion_recursiva.py y vamos a escribir el siguiente código en el que
podremos ver una función que realiza una cuenta atrás recursivamente y al
finalizar muestra un mensaje:
def cuenta_atras(num):
num -= 1
if num > 0:
print(num)
cuenta_atras(num)
else:
Despegue!')
print('
cuenta_atras(5)
python3 funcion_recursiva.py
4
3
2
1
Despegue!
5! = 1 x 2 x 3 x 4 x 5 = 120
def factorial(num):
if num > 1:
71
num *= factorial(num - 1)
return num
print(factorial(5))
python3 funcion_recursiva.py
120
python3 funcion_integrada.py
class '
Aquí la variable numero tiene un valor de 3 de tipo < str'>
Aquí la variable numero tiene un valor de 3 de tipo < class ' int'>
72
print('Aquí la variable numero tiene un valor de {} de tipo
{}'.format(numero, type(numero)))
numero = float(numero)
print('Aquí la variable numero tiene un valor de {} de tipo
{}'.format(numero, type(numero)))
python3 funcion_integrada.py
class '
Aquí la variable numero tiene un valor de 3.14 de tipo < str'>
Aquí la variable numero tiene un valor de 3.14 de tipo < class ' float'>
numero = 3
print('Aquí la variable numero tiene un valor de {} de tipo
{}'.format(numero, type(numero)))
numero = str(numero)
print('Aquí la variable numero tiene un valor de {} de tipo
{}'.format(numero, type(numero)))
python3 funcion_integrada.py
class '
Aquí la variable numero tiene un valor de 3 de tipo < int'>
Aquí la variable numero tiene un valor de 3 de tipo < class ' str'>
Existen otras funciones integradas en Python que nos servirán para convertir
datos numéricos en diferentes bases, es decir, los números en base 10 o
decimales como 1 , 2 , 3 , ... a números en base 2 o binarios
cómo 0 000, 0 001, 0 010, ... mediante la función integrada bin(), por ejemplo:
0
a =
b = 1
c = 2
print('Decimal\tBinario')
print('{}\t{}'.format(a, bin(a)))
print('{}\t{}'.format(b, bin(b)))
print('{}\t{}'.format(c, bin(c)))
python3 funcion_integrada.py
Decimal Binario
0 0b0
1 0b1
2 0b10
73
También podemos convertir un número en base decimal a base 16 o
hexadecimal con la función integrada hex(), por ejemplo:
1
a = 5
b = 2 7
c = 2 09
print('Decimal\tHexadecimal')
print('{}\t{}'.format(a, hex(a)))
print('{}\t{}'.format(b, hex(b)))
print('{}\t{}'.format(c, hex(c)))
python3 funcion_integrada.py
Decimal Hexadecimal
15 0xf
27 0x1b
209 0xd1
print('Bin\tDec')
print('{}\t{}'.format(binario,
int(binario, 2)))
print('-----------')
print('Hex\tDec')
print('{}\t{}'.format(hexadecimal, int(hexadecimal, 16)))
python3 funcion_integrada.py
Bin Dec
0b10 2
-----------
Hex Dec
0xd1 209
74
print(abs(numero))
python3 funcion_integrada.py
17
a = 5.5
b = 5.4
c = 7.1
print(round(a))
print(round(b))
print(round(c))
python3 funcion_integrada.py
6
5
7
print(len(a))
print(len(b))
print(len(c))
print(len(d))
python3 funcion_integrada.py
11
3
4
2
75
Manejo de excepciones
En programación muchas veces podríamos tener un error de retorno a la hora
de evaluar una expresión o de invocar a un método, función, procedimiento o
clase, tanto de nuestro propio código como integrado en el lenguaje, tal y
como ya hemos visto en el punto anterior. Para poder tener un control más
exhaustivo sobre el comportamiento de nuestro programa es muy importante
hacer uso de las excepciones que el lenguaje nos permite, de ese modo
podremos indicarle al programa qué hacer cuando algo no suceda como se
espera. En este punto veremos cómo identificar los errores y cómo
gestionarlos mediante excepciones.
Errores
print('Hola mundo!'
python3 excepciones.py
File "excepciones.py", line 2
^
SyntaxError: unexpected EOF while parsing
Sin embargo, no todos los errores en el código son tan fáciles de identificar,
como pasa con los errores semánticos. Estos errores son los que están
ligados al uso de los recursos que tenemos disponibles para utilizar en nuestro
código. Por ejemplo, algo que para nosotros debería ser correcto en la
76
mayoría de situaciones, pero que bajo una determinada circunstancia podría
no funcionar como se espera. Estos son los errores más difíciles de identificar.
python3 excepciones.py
Traceback (most recent call last):
File "excepciones.py", line 6, in <module>
lista.pop() # Sacamos un cuarto elemento que no existe
IndexError: pop from empty list
print(lista)
77
if len(lista) > 0: # False
lista.pop() # Esta línea no se ejecutará
print(lista) # No se imprimirá valor de lista
python3 excepciones.py
[1, 2, 3]
[1, 2]
[1]
[]
En este ejemplo aparece una parte del código que se repite varias veces, para
optimizarlo lo modificaremos para usar una función recursiva que ejecute una
acción tantas veces como sea necesario hasta que se cumpla la condición de
que la lista tenga una longitud igual a 0 :
def comprueba_lista(l):
print(l)
if len(l) > 0:
l.pop()
comprueba_lista(l)
comprueba_lista(lista)
python3 excepciones.py
[1, 2, 3]
[1, 2]
[1]
[]
Otro erro bastante común es cuando leemos un valor por teclado e intentamos
realizar una operación matemática con ese valor. Todos los valores que se
introducen por teclado son de tipo string a no ser que hagamos un casting y lo
convirtamos en otro tipo diferente. A continuación un ejemplo en el que se
reproduce el error que devuelve el programa al no convertir los datos
introducidos por teclado a un número entero (int) o de coma flotante (float):
i
a = nput('Introduce un número: ')
b = 3
python3 excepciones.py
Introduce un número: 15
78
Traceback (most recent call last):
File "excepciones.py", line 5, in <module>
print('{}/{} = {}'.format(a, b, a/b))
TypeError: unsupported operand type(s) for /: 'str' and 'int'
Como se ha comentado antes, para solventar este error bastaría con hacer
un casting del input, en este caso a número de coma flotante o float de la
siguiente manera:
f
a = loat(input('Introduce un número: '))
b = 3 print('{}/{} = {}'.format(a, b, a/b))
python3 excepciones.py
Introduce un número: 15
15.0/3 = 5.0
Excepciones
try:
79
f
a = loat(input('Introduce un número: '))
b = 3
print('{}/{} = {}'.
format(a, b, a/b))
except:
print('Ha ocurrido un error, introduce bien el número.')
python3 excepciones.py
Introduce un número: abc
Ha ocurrido un error, introduce bien el número.
Como se puede ver, el programa intenta capturar un número por teclado, pero
si el usuario no introduce un número el programa no falla, simplemente ejecuta
el código que hay dentro de e xcept, en este caso solo un p rint() con un
mensaje personalizado de error. Solo hay una pega, tal y como está hecho el
programa ahora mismo, en caso de error solo se imprime un mensaje y el
programa finaliza. Podría interesarnos que además de mostrar ese mensaje
que volviera a pedir introducir un número de forma indefinida hasta que el
usuario introduzca un valor correctamente. Esto lo podemos conseguir
introduciendo el código actual dentro del cuerpo de un bucle while, por
ejemplo:
while True:
try:
f
a = loat(i
nput('Introduce un número: '))
b = 3
{}/{
print(' } = {}'.
format(a, b, a/b))
break # Si todo ha ido bien se interrumpe el bucle.
except:
Ha ocurrido un error, introduce bien el número.')
print('
python3 excepciones.py
Introduce un número: abc
Ha ocurrido un error, introduce bien el número.
Introduce un número: wgr
Ha ocurrido un error, introduce bien el número. Introduce un número: 27
27.0/3 = 9.0
80
while True:
try:
f
a = loat(i
nput('Introduce un número: '))
b = 3
{}/{
print(' } = {}'.
format(a, b, a/b))
except:
Ha ocurrido un error, introduce bien el número.')
print('
else:
Todo ha ido bien, saliendo del programa.')
print('
break # Si todo ha ido bien se interrumpe el bucle.
python3 excepciones.py
Introduce un número: wadf
Ha ocurrido un error, introduce bien el número.
Introduce un número: drgb
Ha ocurrido un error, introduce bien el número.
Introduce un número: 39 39.0/3 = 13.0
Todo ha ido bien, saliendo del programa.
Múltiples excepciones
try:
i
a = nput('Introduce un número: ')
b = 3
print('{}/{} = {}'.
format(a, b, a/b))
except Exception as e:
print(type(e).__name__)
python3 excepciones.py
Introduce un número: 15
TypeError
81
try:
i
a = nput('Introduce un número: ')
b = 3
print('{}/{} = {}'.
format(a, b, a/b))
except TypeError:
print('No se puede dividir una cadena de texto entre un número.')
except Exception as e:
print(type(e).__name__)
python3 excepciones.py
Introduce un número: 15
No se puede dividir una cadena de texto entre un número.
try:
f
a = loat(input('Introduce un número: '))
b = 3
print('{}/{} = {}'.
format(a, b, a/b))
except TypeError:
print('No se puede dividir una cadena de texto entre un número.')
except Exception as e:
print(type(e).__name__)
python3 excepciones.py
Introduce un número: abc
ValueError
Este nuevo error que da cuando intentas convertir a float una cadena de
texto, tiene un identificador llamado ValueError, así que ya podemos hacer otra
excepción personalizada para cuando nuestro programa tenga este tipo de
errores, por ejemplo:
try:
f
a = loat(input('Introduce un número: '))
b = 3
print('{}/{} = {}'.
format(a, b, a/b))
except TypeError:
print('No se puede dividir una cadena de texto entre un número.')
except ValueError:
print('Debes introducir un número.')
except Exception as e:
82
print(type(e).__name__)
python3 excepciones.py
Introduce un número: abc
Debes introducir un número.
try:
f
a = loat(input('Introduce un número: '))
b = 3
print('{}/{} = {}'.
format(b, a, b/a)) # Orden cambiado.
except TypeError:
print('No se puede dividir una cadena de texto entre un número.')
except ValueError:
print('Debes introducir un número.')
except Exception as e:
print(type(e).__name__)
python3 excepciones.py
Introduce un número: 0
ZeroDivisionError
print('{}/{} = {}'.
format(b, a, b/a))
except TypeError:
print('No se puede dividir una cadena de texto entre un número.')
except ValueError:
print('Debes introducir un número.')
except ZeroDivisionError:
print('No se puede dividir por cero.')
except Exception as e:
print(type(e).__name__)
python3 excepciones.py
Introduce un número: 0
83
No se puede dividir por cero.
Invocación de excepciones
Sin duda las excepciones nos ayudan a optimizar nuestros programas y tener
un control más exhaustivo sobre los errores. En este punto veremos cómo se
puede llamar o invocar a una excepción más allá de las maneras que ya
hemos visto.
Vamos a definir una función que reciba un valor, pero si este valor es un valor
especial, por ejemplo un valor nulo, llamaremos a una excepción de
tipo ValueError que ya vimos anteriormente mediante una instrucción
llamada r aise seguida del identificador o tipo de error:
def mi_funcion(algo=None):
if algo is None:
raise V alueError('Error, no se permite un valor nulo.')
mi_funcion()
python3 excepciones.py
Traceback (most recent call last):
File "excepciones.py", line 5, in <module>
mi_funcion()
File "excepciones.py", line 3, in mi_funcion
raise ValueError('Error, no se permite un valor nulo.')
ValueError: Error, no se permite un valor nulo.
Clases y objetos
Con todo lo que conocemos hasta ahora ya podemos crear bastantes
programas. Sin embargo, no dejan de ser instrucciones estructuradas. Esto
significa que cuando queremos solucionar un problema tenemos que pensar
de arriba a abajo, y lo único que nos da un poco de juego son las funciones,
las listas y los diccionarios. Así era la programación en el pasado, bastante
aburrida, con mucho código, mucha gestión de recursos y muy difícil de
84
mantener. Hasta que poco a poco fue tomando forma un paradigma de
programación conocido como Programación Orientada a Objetos (POO).
El caso de estudio será el siguiente: Nos piden crear un registro para manejar
los clientes de una empresa, con su nombre, sus apellidos y su DNI. El
programa debe permitirnos mostrar los datos de los clientes o eliminarlos a
partir de su DNI. En un primer momento podremos pensar que lo más
razonable es trabajar con ficheros o bases de datos, pero todavía no hemos
llegado a esa parte, de momento trabajaremos con almacenamiento de
información en memoria, es decir durante la ejecución del programa.
[
clientes =
Richard', 'apellidos': 'Stallman', 'dni': '01234567A'},
{'nombre': '
{'nombre': ' Aaron', 'apellidos': 'Swartz', 'dni': '98765432Z'}
]
85
print(clientes)
python3 clientes.py
[{'nombre': 'Richard', ' apellidos': 'Stallman', 'dni': '01234567A'},
{'nombre': 'Aaron', 'apellidos': 'Swartz', 'dni': '98765432Z'}]
print('Clente no encontrado.')
python3 clientes.py
Richard Stallman
Muestra el nombre y apellidos del cliente que coincide con el DNI 01234567A.
Vamos a modificar el DNI que estamos poniendo a la hora de invocar la
función m ostrar_cliente poniendo un DNI que no exista en nuestra
lista c lientes, por ejemplo:
mostrar_cliente(clientes, '00000000B')
python3 clientes.py
Clente no encontrado.
El bucle for completó todas las iteraciones buscando algún cliente con el
DNI 00000000B y como no se encontró ningún resultado se muestra por pantalla
el mensaje correspondiente. Se puede decir que ya tenemos una aplicación
con una funcionalidad o método muy rudimentario que se encarga de buscar
clientes dentro de una lista. Hagamos una nueva funcionalidad que se
encargue de borrar un cliente de la lista, para ello haremos una nueva función
86
llamada borrar_cliente que reciba también dos argumentos, la lista de clientes
y el DNI del cliente que se quiere borrar, por ejemplo:
print('Cliente no encontrado.')
print(clientes)
borrar_cliente(clientes, '01234567A')
print(clientes)
python3 clientes.py
[{'nombre': 'Richard', ' apellidos': 'Stallman', 'dni': '01234567A'},
{'nombre': 'Aaron', 'apellidos': 'Swartz', 'dni': '98765432Z'}]
{'nombre': 'Richard', 'apellidos': ' Stallman', 'dni': '01234567A'}-->Borrado
[{'nombre': 'Aaron', 'apellidos': 'Swartz', 'dni': '98765432Z'}]
87
de este tipo de programación es que a la larga se hace muy confuso porque se
generaría mucho código y esto es difícil de mantener. Además, como hemos
visto, tendríamos que estar moviendo constantemente la lista de clientes de un
lado a otro para poder manejarla y ejecutar acciones sobre ella, esto no es
muy práctico.
class Cliente:
def __str__(self):
return '{} {}'.
format(self.nombre, self.apellidos)
class Empresa:
88
especial, es el constructor de la clase, es decir, donde se va a otorgar valores
a los atributos de la clase. En el caso de la clase Cliente existen tres atributos,
que son el dni, nombre y apellidos. Para indicar que son atributos de la clase
deben llevar el prefijo s elf.. En el caso de la clase Empresa solo hay un
atributo c lientes. Si este no se especifica tendrá como valor por defecto una
lista vacía.
En lugar de trabajar con funciones, como era el caso de la programación
estructurada, aquí cambia la sintaxis, cada clase tendrá sus propios métodos.
Lo primero que tenemos que hacer a continuación del código anterior es crear
un cliente en memoria, esto se realiza de la siguiente manera:
Vamos a crear otro cliente pero esta vez sin especificar el nombre de los
atributos, solo respetando el orden en el que la clase Cliente espera recibir los
argumentos, que son dni, nombre y apellidos:
Con estas dos nuevas líneas habremos creado dos instancias u objetos de la
clase Cliente, cada una con sus atributos personalizados.
Ahora vamos a crear una instancia u objeto de la clase Empresa, esta clase
necesita recibir como argumento una lista, lo haremos de la siguiente manera:
Vamos a añadir una línea más para imprimir el atributo clientes de la instancia
de la clase Empresa que hemos llamado empresa:
print(empresa.clientes)
python3 clientes_poo.py
[<__main__.Cliente object at 0x7fad555e9940>,
<__main__.Cliente object at 0x7fad555e9978>]
89
este caso concreto de la clase Cliente. Pero de esta manera no podremos ver
más que la dirección de memoria hexadecimal en la que fueron alojados
sendos objetos, ¿cómo podríamos acceder a su atributos? Como por ejemplo
el nombre, apellidos o el dni. La clase Empresa tiene un método
llamado m ostrar_cliente que recibe por parámetro el dni de un cliente.
Para hacer uso de un método de una clase debemos poner el nombre del
objeto seguido de un punto (.) y el nombre del método con paréntesis y los
valores que correspondan dentro de los paréntesis, por ejemplo:
empresa.mostrar_cliente(dni='00000000A')
python3 clientes_poo.py
[<__main__.Cliente object at 0x7f3b32aaf9b0>,
<__main__.Cliente object at 0x7f3b32aaf9e8>]
Javier Dominguez
empresa.borrar_cliente(dni='11111111B')
print(empresa.clientes)
python3 clientes_poo.py
[<__main__.Cliente object at 0x7f3b32aaf9b0>,
<__main__.Cliente object at 0x7f3b32aaf9e8>]
Javier Dominguez
Beatriz Gimenez --> Borrado
[<__main__.Cliente object at 0x7f3b32aaf9b0>]
En este punto puede que no tengamos muy claro que está sucediendo por
detrás durante la ejecución del programa, pero podemos notar una sintaxis
más clara y auto explicativa que nos ayuda a comprender el programa. Tanto
la empresa como los clientes tienen su propia clase con sus atributos y sus
funciones. Podemos rellenar la lista de clientes de la empresa con objetos de
la clase cliente, cada uno con sus propios atributos y podemos hacer que
interaccionan unos objetos con otros entre clases. En resumen podríamos
decir que la programación orientada a objetos se basa en determinar las
90
entidades con nombres propios en lugar de crear estructuras y diccionarios
para representar la información.
Clases y objetos
En cualquier lenguaje de programación si hablamos de objetos es necesario
hablar también de clases. De hecho sin clases no habría objetos, ya que las
clases son los moldes de los objetos, en cierto modo como lo son un molde de
galletas y las propias galletas. Para ver algunos ejemplos vamos a crear un
nuevo archivo llamado c lases_y_objetos.py y vamos a crear la siguiente clase
llamada G alleta que no contendrá nada, es una clase vacía, para ello usamos
la instrucción p ass:
class Galleta:
pass
A continuación vamos a escribir estas dos líneas para crear dos objetos de
esta clase:
Galleta()
galleta_1 =
galleta_2 = Galleta()
Se podría decir que hemos creado dos galletas galleta_1 y galleta_2 a partir
del molde Galleta. Cada vez que creamos un objeto de una clase lo que
estamos haciendo es instanciar un objeto. El concepto de instancia es muy
importante, ya que hace referencia a algo que existe en la memoria del
ordenador, pero solo mientras el programa se está ejecutando. Cuando el
programa finaliza toda esta información se libera de la memoria y desaparece.
es lo contrario a una base de datos, en cuyo caso la información permanecerá
almacenada en la base de datos e incluso si el programa finaliza, es lo que se
denomina como persistencia de datos. Hablaremos de ello más adelante.
print(type(galleta_1))
python3 clases_y_objetos.py
<class '__main__.Galleta'>
91
print(type(10))
print(type(3.14))
print(type('Hola'))
print(type([]))
print(type({}))
print(type(True))
def hola():
pass
print(type(hola))
python3 clases_y_objetos.py
<class '__main__.Galleta'>
<class 'int'>
<class 'float'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'bool'>
<class 'function'>
Para estudiar este punto seguiremos con el ejemplo del molde de galletas y las
galletas que salen de él, no todas serán iguales. Siguiendo con la metáfora,
todas las galletas compartirán la misma forma y tamaño, pero pueden hacerse
de diferentes colores, sabores o ingredientes. He ahí el que sus atributos
tengan diferentes valores y que cada galleta sea única.
92
verlo vamos a crear un nuevo archivo llamado galletas.py con el siguiente
código:
class Galleta:
pass
g = Galleta()
g.sabor = 'salada'
g.color = 'amarillo'
python3 galletas.py
Esta galleta es de color amarillo y sabe salada
Al objeto g de la clase Galleta acabamos de asignar dos atributos que no tenía
cuando fue creado. No es una manera muy recomendable de hacerlo, ya que
si creamos un nuevo objeto de la clase Galleta este no tendría esos atributos
por defecto y tendríamos que gestionar si queremos que todas las galletas los
tengan o no, aunque tengan diferentes valores. Para ello es mejor definir los
atributos en el cuerpo de la clase, de modo que todos los objetos creados de
esta clase tendrán los mismos atributos, por ejemplo:
class Galleta:
sabor = '
salada'
color = ' amarillo'
Galleta()
g1 =
g2 = Galleta()
python3 galletas.py
La galleta 1 es de color amarillo y sabe salada
La galleta 2 es de color amarillo y sabe salada
Como podemos ver, no es necesario asignar atributos cada vez que se crea un
objeto de la clase Galleta, ya se definen en la propia clase. Lo malo de este
método es que todas tendrán los mismos valores por defecto, por lo que si
saldrían todas las galletas iguales.
Para que cada objeto tenga sus propios valores de atributos lo mejor es
definirlos en el momento que se crea el objeto, para ello es necesario conocer
dos conceptos nuevos de las clases, uno es el método especial
llamado _ _init__(), y el otro es la palabra reservada self. Vamos a desarrollar
de nuevo la clase introduciendo estos dos nuevos conceptos:
93
class Galleta:
Galleta('salada', '
g1 = amarilla')
g2 = Galleta('dulce', 'verde')
Añadimos dos líneas para imprimir un mensaje por pantalla donde se puedan
ver los atributos de las instancias g1 y g2, para acceder al dato escribiremos a
continuación del nombre del objeto un punto y el nombre del atributo de ese
objeto:
class Galleta:
def chocolatear(self):
self.chocolate = True
94
def tiene_chocolate(self):
if self.chocolate:
Si tiene chocolate')
print('
else:
print(' No tiene chocolate')
Galleta('salada', '
g1 = amarilla')
g2 = Galleta('dulce', 'verde')
python galletas.py
95
Métodos especiales
class Pelicula:
# Contructor de clase.
def __init__(self, titulo, duracion, lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
Se ha creado la película {}'. format(self.titulo))
print('
python peliculas.py
Se ha creado la película El padrino
# Destructor de la clase
def __del__(self):
Se está borrando la película {}'.format(self.titulo))
print('
del(p)
96
Otro método especial es el método llamado __str__() que servirá para aquellas
ocasiones en las que se pasa el nombre del objeto por la función
integrada s tr() de Python, en cuyo caso ya no se mostrará una cadena con la
dirección de memoria en la que está almacenado el objeto, si no un mensaje
personalizado. Por ejemplo:
# Redefinimos el método string
def __str__(self):
return '{} lanzada en {} con una duración de {}
minutos'.format(self.titulo, self.lanzamiento, s
elf.duracion)
print(str(p))
python peliculas.py
Se ha creado la película El padrino
El padrino lanzada en 1972 con una duración de 175 minutos
Se está borrando la película El padrino
En Python podemos crear objetos que tengan atributos cuyo valor pueden ser
objetos de otras clases. Al principio de esta sección hemos visto un ejemplo
en el que usábamos la clase Cliente y la clase Empresa. En este ejemplo el
objeto de la clase E mpresa contenía una lista de objetos de la clase Cliente.
Ahora vamos a hacer un catálogo para poder manejar las películas del ejemplo
anterior. Modificamos ligeramente la clase P elicula que teníamos definida en el
archivo p eliculas.py del punto anterior. Eliminemos el método especial
destructor y cambiemos el mensaje que devuelve el método
especial _ _str__(), por ejemplo:
97
class Pelicula:
# Contructor de clase.
def __init__(self, titulo, duracion, lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
Se ha creado la película {}'.format(self.titulo))
print('
Hagamos a continuación una nueva clase llamada Catalogo, esta clase tendrá
un atributo llamado peliculas y tendrá por valor una lista vacía. Además tendrá
un método constructor que inicializa la lista de películas vacía si no se le
especifica ninguna lista o con una ya existente si se especificara como
argumento a la hora de instanciar el objeto de la clase C atalogo. También
tendrá dos métodos, el método a gregar() que simplemente hace un a ppend a la
lista p eliculas de la película que se le pase como argumento, y el
método m ostrar(), que simplemente hace un p rint() del objeto película.
class Catalogo:
peliculas = []
def mostrar(self):
for p in self.peliculas:
print(p)
c = Catalogo([p1])
98
c.agregar(Pelicula('El padrino II', 202, 1974))
c.mostrar()
python peliculas.py
Se ha creado la película El padrino
Se ha creado la película El padrino II
El padrino (1972)
El padrino II (1974)
Encapsulación de atributos
Hasta ahora hemos visto cómo en Python se puede acceder a los atributos y
métodos de una clase de una manera muy sencilla. Esto es por que tal y como
los hemos visto tienen un acceso público, pero en algunas ocasiones podría
interesarnos que no fuera así, y que estos no se puedan ejecutar desde fuera,
en cuyo caso tendrían un acceso privado, que permanezcan encapsulados.
class Ejemplo:
_atributo_privado = 'Soy un atributo inalcanzable desde fuera.'
def _metodo_privado(self):
Soy un método inalcanzable desde fuera.')
print('
Ahora vamos a crear una instancia de esta clase y vamos a intentar acceder a
su atributo _atributo_privado desde fuera:
print(e._atributo_privado)
99
Si ejecutamos el programa ahora obtendremos el siguiente error:
python3 encapsulacion.py
Traceback (most recent call last):
File "encapsulacion.py", line 9, i
n <module>
print(e._atributo_privado)
AttributeError: 'Ejemplo' object has no attribute ' _atributo_privado'
e._metodo_privado()
encapsulacion.py
Traceback (most recent call last):
File "01_introduccion/01_10_src/encapsulacion.py", line 9, in <module>
e._metodo_privado()
AttributeError: 'Ejemplo' object has no attribute '
_metodo_privado'
Para poder hacer uso de los atributos y métodos privados desde fuera
tendremos que crear un nuevo método público que haga de puente y que sea
él el que tiene acceso a los elementos privados desde dentro de la clase, por
ejemplo:
def mostrar_atributo(self):
return self._atributo_privado
print(e.mostrar_atributo())
python3 encapsulacion.py
Soy un atributo inalcanzable desde fuera.
100
Aplicando la misma técnica al método podremos acceder también al método
privado creando una nuevo método público mostrar_metodo() que haga de
puente, por ejemplo:
def mostrar_metodo(self):
return self._metodo_privado()
e.mostrar_metodo()
python3 encapsulacion.py
Soy un método inalcanzable desde fuera.
Herencia
Para verlo en un ejemplo vamos a crear una aplicación para la gestión de una
tienda en la que se venderán diferentes productos. Nuestro programa estará
compuesto por varios archivos con código fuente Python en el que vamos a ir
añadiendo diferente código. Empecemos por crear un archivo
llamado p roducto.py, aquí definiremos la clase Producto donde definiremos
todos los campos posibles relativos a los tipos de producto que podamos
crear, tales como alimentos o libros:
class Producto:
def __init__(self, referencia, tipo, nombre, pvp, descripcion,
productor=None, distribuidor=None, autor=None):
self.referencia = referencia
self.tipo = tipo
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion
self.productor = productor
self.distribuidor = distribuidor
self.autor = autor
101
Este sería un buen comienzo para crear productos, de momento una clase
llamada Producto que solo tiene un método constructor __init__() en el que se
definirá el valor de unos atributos obligatorios,
como r eferencia, t ipo, n ombre, p vp y descripcion, y luego otros atributos
opcionales, como p roductor, d istribuidor y a utor.
Los atributos obligatorios son aquellos que tendremos que especificar siempre
que definamos una instancia u objetos de la clase, en este caso porque son
todos los atributos que todos los productos tendrán en común. Sin embargo
los opcionales podremos especificarlos o no, dependerá del tipo de producto
que queremos instanciar. En los casos en los que no especifiquemos estos
atributos opcionales se le asignará un valor por defecto N one.
Vamos a crear un producto instanciando un objeto de esta clase, por ejemplo:
adorno = Producto('000A', 'Adorno', 'Jarrón', 15, 'Jarrón de porcelana con
dibujos')
print(adorno.tipo)
print(adorno.descripcion)
python3 producto.py
Adorno
Jarrón de porcelana con dibujos
De momento tenemos algo que podría servirnos, pero no tiene mucho sentido
mezclar tantos atributos tan diferentes como isbn y productor. Además, al crear
un producto de cualquier tipo tendremos que recorrer y establecer valor a
todos los atributos. Esto es poco eficiente, por lo que necesitaremos
establecer de alguna manera una jerarquía y poner orden. La herencia de
clases nos puede servir en estos casos, para ello tendremos que dividir el
código en una Superclase y diferentes Subclases. En una Superclase
agrupamos todos los atributos que sean comunes para todos los productos,
por ejemplo en la clase P roducto:
class Producto:
def __init__(self, referencia, nombre, pvp, descripcion):
self.referencia = referencia
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion
A esta clase Producto podemos añadirle el método especial __str__() para que
devuelva una descripción del producto, por ejemplo:
102
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}""".format(self.referencia, self.nombre,
self.pvp, self.descripcion)
class Adorno(Producto):
pass
python3 producto.py
REFERENCIA 00000
NOMBRE Jarrón
PVP 15.5
DESCRIPCIÓN Jarrón de porcelana con dibujos
class Alimento(Producto):
productor = ''
distribuidor = ''
103
Añadimos valor a sus dos atributos e imprimimos el objeto:
print(al)
python3 producto.py
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
Si nos fijamos bien se imprimen solo los datos que se indican en la función
especial __str__() de la clase madre Producto, y este método especial no
incluye los atributos propios de cada subclase. Para solucionar esto podremos
redefinir el método especial _ _str__() en cada subclase, por ejemplo en el
caso de la subclase A limento lo haríamos de la siguiente manera:
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}
PRODUCTOR\t{}
DISTRIBUIDOR\t{}""".format(self.referencia, self.nombre,
self.pvp, self.descripcion, self.productor, self.distribuidor)
python3 producto.py
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
PRODUCTOR La aceitera
DISTRIBUIDOR Distribuciones S.A.
class Libro(Producto):
isbn = ''
autor = ''
104
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}
PRODUCTOR\t{}
AUTOR\t{}""".format(self.referencia, self.nombre, self.pvp,
self.descripcion, self.isbn, self.autor)
python3 producto.py
REFERENCIA 00002
NOMBRE El enemigo conoce el sistema
PVP 17.9
DESCRIPCIÓN Libro sobre redes de hiper vigilancia
PRODUCTOR 8417636390
AUTOR Marta Peirano
Clases heredadas
A esta lista podremos añadirle otros objetos que vayamos creando o que ya
hemos creado, por ejemplo el objeto de tipo Adorno que creamos al principio:
productos.append(ad)
python3 producto.py
105
[<__main__.Alimento object at 0x10b341eb8>,
<__main__.Libro object at 0x10b341ef0>,
<__main__.Adorno object at 0x10b341e80>]
Ahora podremos recorrer esta lista cómodamente con un bucle for, por
ejemplo:
python3 producto.py
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
PRODUCTOR La aceitera
DISTRIBUIDOR Distribuciones S.A.
REFERENCIA 00002
NOMBRE El enemigo conoce el sistema
PVP 17.9
DESCRIPCIÓN Libro sobre redes de hiper vigilancia
PRODUCTOR 8417636390
DISTRIBUIDOR Marta Peirano
REFERENCIA 00000
NOMBRE Jarrón
PVP 15.5
DESCRIPCIÓN Jarrón de porcelana con dibujos
python3 producto.py
00001 Aceite de Oliva
00002 El enemigo conoce el sistema
00000 Jarrón
106
Para gestionar objetos de distintas clases con diferentes atributos lo mejor
será utilizar algunos condicionales en cada iteración y una la función
integrada isinstance() para comprobar si un objeto es una instancia de una
clase determinada. Veamos el siguiente ejemplo:
python3 producto.py
00001 Aceite de Oliva La aceitera
00002 El enemigo conoce el sistema 8417636390
00000 Jarrón
Una cosa interesante que podemos hacer es crear una función que reciba un
producto y modifique alguno de sus atributos. Por ejemplo, una función que
rebaje un tanto por ciento el precio de los productos. Veamos un ejemplo con
el producto a l de tipo A limento que creamos anteriormente. Primero crearemos
una nueva función llamada r ebajar_producto() con el siguiente código:
print(al_rebajado)
python3 producto.py
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 4.5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
PRODUCTOR La aceitera
107
DISTRIBUIDOR Distribuciones S.A.
print(al_rebajado)
print(al)
python3 producto.py
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 4.5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
PRODUCTOR La aceitera
DISTRIBUIDOR Distribuciones S.A.
REFERENCIA 00001
NOMBRE Aceite de Oliva
PVP 4.5
DESCRIPCIÓN Botella de aceite de oliva virgen extra
PRODUCTOR La aceitera
DISTRIBUIDOR Distribuciones S.A.
Esto sucede porque los objetos, al igual que las colecciones, se pueden pasar
como argumentos a las funciones pero se pasan por referencia, es decir, el
original, no se realiza una copia de su valor. Recordemos el pase por
referencia en las listas:
python3 ejemplo.py
[1, 2, 3, 4]
En una lista para copiar el valor debíamos usar los corchetes con dos puntos
dentro ([:]), de ese modo el append se realizaría sobre la copia, y no sobre
una referencia al original.
Con los objetos pasa lo mismo, solo que para hacer una copia de un objeto
tendremos que usar un método perteneciente a un módulo externo
llamado c opy() que primero deberemos importar para poder utilizarlo. Al
principio del código debemos escribir la siguiente línea:
import copy
108
modifiquemos el objeto original no se vea afectado por el cambio. Veamos un
ejemplo con el objeto ad de tipo Adorno:
print(ad)
print(copia_adorno)
python3 producto.py
REFERENCIA 00000
NOMBRE Jarrón
PVP 15.5
DESCRIPCIÓN Jarrón de porcelana con dibujos
REFERENCIA 00000
NOMBRE Jarrón
PVP 16.25
DESCRIPCIÓN Jarrón de porcelana con dibujos
Herencia múltiple
class A:
def __init__(self):
Soy de clase A')
print('
109
A continuación haremos una clase B con el mismo código que la clase A a
diferencia del mensaje, este dirá 'Soy de clase B':
class B:
def __init__(self):
Soy de clase B')
print('
Por último haremos una clase C que herede de la clase A y B, en este orden, y
la clase no hará nada, solo un pass en su cuerpo:
c = C()
python3 herencia_multiple.py
Soy de clase A
Ha heredado el mismo método especial __init__() de las clases A y B, pero
solo ha mostrado el mensaje de la clase A. Probemos a cambiar el orden de la
herencia múltiple del siguiente modo:
python3 herencia_multiple.py
Soy de clase B
110
Para que el estudio de estos métodos sea más ágil y eficaz trabajaremos
desde la consola de Python en vez de escribir los ejemplos en un archivo. De
este modo se podrá experimentar en tiempo real lo que sucede con los datos
y al invocar los métodos que vayamos utilizando.
python3
Python 3.7.0 (default, Jun 28 2 018, 0
7:39:16)
Type "help", "copyright", "credits" o r "license" for more information.
>>>
Tras los tres carácteres >>> ya podemos empezar a escribir código Python.
Para salir bastaría con escribir el método exit():
>>> exit()
111
Hola mundo'.count('o')
>>> '
2
>>> ' Hola mundo'.count('mundo')
1
112
Comprobar si todos los carácteres son letras mayúsculas:
>>> c = 'HOLA MUNDO'
>>> c.isupper()
True
>>> c.endswith('o')
True
>>> c.endswith('mundo')
True
Separar una cadena en una lista de subcadenas a partir un caracter que haga
de delimitador, por ejemplo el espacio:
>>> c ='aaa;bbb;ccc'
>>> c.split(';')
['aaa', 'bbb', 'ccc']
113
Añadir un carácter o subcadena entre cada carácter de una cadena, por
ejemplo una coma o un guión bajo:
>>> ','.join(c)
'H,o,l,a, ,m,u,n,d,o'
>>> '_'.join(c)
'H_o_l_a_ _m_u_n_d_o'
Métodos en listas
Añadir elementos a una lista:
>>> l = [1, 2, 3]
>>> l.append(4)
>>> l
[1, 2, 3, 4]
114
>>> l = [1, 2, 3]
>>> l.clear()
>>> l
[]
Mostrar la posición del índice en la que aparece por primera vez un elemento
en una lista.
>>> l = ['Hola', 'mundo', 'mundo']
>>> l.index('Hola')
0
>>> l.index('mundo')
1
115
>>> l = ['uno', 'dos', '
tres']
>>> l
['uno', 'dos', 'tres']
>>> l.remove('dos')
>>> l
['uno', 'tres']
Métodos en conjuntos
Añadir elementos a un conjunto:
116
{'dos', 'uno', 'tres'}
>>> c.discard('dos')
>>> c
{'uno', 'tres'}
117
Unión de dos conjuntos. Si hay elementos repetidos estos no se añaden varias
veces:
>>> c1 = {1, 2, 3, 4 , 5 }
>>> c2 = {3, 4, 5, 6 , 7 }
>>> c1.union(c2)
{1, 2, 3, 4, 5, 6, 7 }
Pero esto no actualiza el valor de ningún conjunto, solo muestra por pantalla el
resultado de la unión. Si vemos lo que contienen los conjuntos c1 y c2 veremos
que no han mutado:
>>> c1
{1, 2, 3 , 4 , 5 }
>>> c2
{3, 4, 5 , 6 , 7 }
Para que se actualice el valor del primer conjunto con la unión de ambos
conjuntos como valor se ha de usar el método update() de la siguiente manera:
Encontrar elementos que no son comunes o que son distintos entre dos
conjuntos:
118
>>> c2 = {3, 4, 5}
>>> c1.intersection(c2)
{3}
Al igual que antes con el método difference() este solo devuelve un resultado,
pero no actualiza el valor de ningún conjunto:
>>> c1 {1, 2
, 3 }
>>> c2 {3, 4 , 5 }
Métodos en diccionarios
Obtener un valor por defecto cuando queremos acceder a una clave que no
existe en un diccionario:
>>> colores = {'amarillo': '
yellow', 'azul': 'blue', 'verde': 'green'}
>>> colores.get('amarillo', 'No se encuentra')
'yellow'
>>> colores.get('negro', 'No se encuentra')
'No se encuentra'
119
Sustraer o eliminar una clave y su valor de un diccionario:
Módulos y paquetes
Los módulos son archivos que contienen definiciones y declaraciones en
lenguaje Python. De esta manera es posible importarlos en otros scripts o
programas y reutilizar estas funcionalidades y a la vez conseguiremos crear
una jerarquía mucho más práctica en nuestros proyectos conteniendo los
módulos en paquetes.
En esta unidad vamos a ver cómo podemos crear nuestros propios módulos y
paquetes en Python, así como su manejo, y un repaso por algunos de los
módulos standard más útiles.
Módulos
Vamos a crear un módulo que tendrá la única funcionalidad de saludar
mediante un mensaje por pantalla. Para ello crearemos un archivo
llamado saludos.py con la siguiente función:
def saludar():
print('Hola, te estoy saludando desde la función saludar del módulo
saludos')
120
Ahora crearemos en el mismo directorio otro archivo llamado test.py donde
importaremos el módulo saludos de la siguiente manera:
import saludos
Una vez que ya tenemos importado el módulo saludos ya podemos hacer uso
de sus funciones, pero no podemos hacerlo del mismo modo que cuando
teníamos funciones definidas en nuestro archivo principal, en estos casos hay
que hacerlo anteponiendo el nombre del módulo y un punto (.), por ejemplo:
saludos.saludar()
python3 test.py
Hola, te estoy saludando desde la función saludar del módulo saludos
Para no tener que anteponer el nombre del módulo cada vez que queramos
invocar a una de sus funciones se puede importar la función o funciones
concretas de un módulo de la siguiente manera:
from saludos import saludar
saludar()
python3 test.py
Hola, te estoy saludando desde la función saludar del módulo saludos
class Saludo():
def __init__(self):
print('Hola, te estoy saludando desde el __init__() de la clase
Saludo')
121
Ahora en el archivo test.py podríamos importar el módulo entero y hacer uso
de la clase Saludo de la siguiente manera:
import saludos
saludos.Saludo()
python3 test.py
Hola, te estoy saludando desde el __init__() de la clase Saludo
También podríamos importar solo la clase Saludo tal y como hemos visto antes:
python3 test.py
Hola, te estoy saludando desde el __init__() de la clase Saludo
Collections
print(Counter(l))
python3 test_collections.py
Counter({1: 4, 3: 3, 2: 1, 4: 1, 5: 1, 6: 1})
En este resultado se puede observar que el número 1 aparece cuatro veces, el
número 3 tres veces, el número 2 una vez, etc.
Otro ejemplo en el uso de la colección Counter es contar las veces que aparece
un carácter en una cadena de caracteres o string:
122
print(Counter(p))
python3 test_collections.py
Counter({'o': 2, 'H': 1, 'l': 1, 'a': 1, ' ': 1, 'm': 1, 'u': 1, 'n': 1, 'd':
1, '!': 1})
En este caso el programa nos dice que el carácter 'o' aparece dos veces, el
carácter 'H' una vez, el caracter 'l' una vez, etc.
Podría darse el caso en el que tenemos una cadena de caracteres con una
varias palabras separadas por un espacio, por ejemplo:
En este caso queremos saber cuántas veces aparece cada palabra, pero hay
que precisar que esto no es una lista de palabras, sino una cadena de
carácteres. Para resolverlo primero podemos pasar esta cadena de caracteres
compuesta de palabras separadas por espacio a una lista, y para ello
podemos usar la función integrada s plit(), al que si no le pasamos ningún
argumento tomará el carácter espacio por defecto:
print(s.split())
python3 test_collections.py
['rojo', 'verde', 'azul', 'rojo', 'morado', 'rojo', 'blanco', 'blanco']
print(Counter(s.split()))
python3 test_collections.py
Counter({'rojo': 3, 'blanco': 2, 'verde': 1, 'azul': 1, 'morado': 1})
n = [10, 20, 30, 40, 10, 20, 30, 10, 20, 10]
c = Counter(n)
print(c.most_common(1))
123
Nos dice que el elemento más común es el número 10, que aparece cuatro
veces. Si a la función integrada most_common() le pasamos como argumento el
número 2 nos devolverá los dos elementos más comunes:
print(c.most_common(2))
python3 test_collections.py
[(10, 4), (20, 3)]
print(OrderedDict(d))
python3 test_collections.py
dog'), ('gato', 'cat'), ('loro', 'parrot')])
OrderedDict([('perro', '
Datetime
Uno de los módulos más interesantes sin duda es datetime, que nos servirá
para manejar y trabajar con información relacionada con las fechas. Para
trabajar sobre este punto vamos a crear un nuevo archivo
llamado t est_datetime.py y vamos a comenzar importando este módulo de la
siguiente manera:
import datetime
Ahora vamos a crear un objeto de tipo datetime en el que haremos uso del
subpaquete datetime y su método now():
dt = datetime.datetime.now()
124
print(dt)
python3 test_datetime.py
2019-10-04 14:10:50.879112
De esta manera la variable dt nos devuelve la fecha y hora actual. También
podemos acceder a cada uno de los atributos del objeto dt, como solo el año,
el mes, día, hora, minuto, segundo o microsegundos, de la siguiente manera:
print(dt.year)
print(dt.month)
print(dt.day)
print(dt.hour)
print(dt.minute)
print(dt.second)
print(dt.microsecond)
python3 test_datetime.py
2019
10
4
14
25
45
315492
Ahora que ya sabemos cómo podemos acceder a cada uno de los elementos
de los que se compone una fecha y hora podremos darle el formato que se
prefiera, por ejemplo:
print('{}/{}/{}'.format(dt.year, dt.month, dt.day))
print('{}:{}:{} {}'.format(dt.hour, dt.minute, dt.second, dt.microsecond))
python3 test_datetime.py
2019/10/5 8:14:11 307892
print(dt)
python3 test_datetime.py
2000-01-01 00:00:00
125
almacenan en una tupla, y las tuplas son inmutables. Por ejemplo, si queremos
cambiar el año asignando un nuevo valor al atributo year de la siguiente forma
nos daría el siguiente error:
python3 test_datetime.py
Traceback (most recent call last):
File "test_datetime.py", line 5, i
n <module>
dt.year = 3000
AttributeError: attribute 'year' of 'datetime.date' objects is not writable
dt = dt.replace(year=3000)
print(dt)
python3 test_datetime.py
3000-01-01 00:00:00
dt = datetime.datetime.now()
print(dt.isoformat())
python3 test_datetime.py
2019-10-07T19:46:08.409881
python3 test_datetime.py
Monday 07 October 2019 07:52
import locale
126
locale.setlocale(locale.LC_ALL, 'es_ES')
locale.setlocale(locale.LC_ALL, 'zh_CN')
python3 test_datetime.py
星期一 07 十月 2019 20:01
Math
El módulo math integra una serie de funciones y métodos que nos servirán para
realizar algunas operaciones matemáticas de forma más sencilla. Al igual que
otros módulos será necesario importarlo al inicio de nuestro código, así que
vamos a hacerlo en un nuevo archivo llamado t est_math.py:
import math
print(round(1.4))
python3 test_math.py
1
print(round(1.5))
python3 test_math.py
2
Gracias al método floor() del módulo math podremos forzar que el redondeo
sea siempre a la baja, por ejemplo:
127
print(math.floor(1.3))
print(math.floor(1.5))
print(math.floor(1.9))
python3 test_math.py
1
1
1
print(math.ceil(1.00001))
print(math.ceil(1.3))
print(math.ceil(1.8))
python3 test_math.py
2
2
2
print(math.fsum(numeros))
python3 test_math.py
15.0
Si bien es cierto que existe un método integrado de Python llamado sum() que
ya hace un sumatorio de una lista de números, esta no es igual de eficaz, ya
que si se suman números enteros y flotantes tiene un comportamiento extraño,
por ejemplo:
print(sum(numeros))
print(math.fsum(numeros))
python3 test_math.py
6.999999900000001
6.9999999
128
También es interesante es el método trunc(), que trunca un número decimal y
devuelve la parte entera, por ejemplo:
print(math.trunc(3.14159265359))
python3 test_math.py
3
print(math.pow(2, 3
))
print(math.pow(5, 4 ))
python3 test_math.py
8.0
625.0
print(math.sqrt(9))
python3 test_math.py
3.0
Además de métodos también tiene algunos atributos como las constantes del
número pi o el número e:
print(math.pi)
print(math.e)
python3 test_math.py
3.141592653589793
2.718281828459045
Random
129
grado de seguridad. Veamos algunos ejemplos en un nuevo archivo
llamado test_random.py. Lo primero que hay que hacer es importar el
módulo r andom de la siguiente manera:
import random
print(random.random())
print(random.random())
print(random.random())
python3 test_random.py
0.06823749155608883
0.9070119606268106
0.4445508707984924
1
print(random.uniform(1, 0))
print(random.uniform(1, 1 0))
print(random.uniform(1, 1 0))
python3 test_random.py
2.382198826548743
4.8697236381240865
3.8781201434396864
print(random.randrange(10))
print(random.randrange(10))
print(random.randrange(10))
python3 test_random.py
8
1
4
También podemos pasarle dos números como argumentos para que devuelva
un número aleatorio entre esos números, por ejemplo:
print(random.randrange(0, 100))
130
print(random.randrange(0, 1
00))
print(random.randrange(0, 1 00))
python3 test_random.py
95
52
91
Y si añadimos un número 2 como tercer argumento solo nos sacará números
random pares entre cero y diez:
print(random.randrange(0, 1
00, 2 ))
print(random.randrange(0, 1 00, 2 ))
print(random.randrange(0, 1 00, 2 ))
python3 test_random.py
94
46
32
print(random.choice(cadena))
print(random.choice(cadena))
print(random.choice(cadena))
python3 test_random.py
n
u
o
El método choice() también nos vale para listas, en este caso se obtendría de
forma aleatoria cualquiera de los elementos de la lista, por ejemplo:
print(random.choice(lista))
print(random.choice(lista))
print(random.choice(lista))
python3 test_random.py
4
5
3
131
Además del método choice() también podremos usar un método
llamado shuffle() para desordenar los elementos de una lista y que
permanezcan guardados de ese modo en la lista de origen, mezcla los
elementos de forma aleatoria, por ejemplo:
print(lista)
random.shuffle(lista)
print(lista)
python3 test_random.py
[1, 2, 3, 4, 5]
[3, 4, 1, 2, 5]
))
print(random.sample(lista, 3
print(random.sample(lista, 3 ))
print(random.sample(lista, 3 ))
python3 test_random.py
[4, 5, 1]
[4, 2, 5]
[3, 4, 2]
Paquetes
Utilizar paquetes nos ofrece varias ventajas. En primer lugar nos permite
unificar distintos módulos bajo un mismo número de paquetes. Así podemos
utilizar jerarquías de módulos o submódulos y también subpaquetes. Por otra
parte nos permiten distribuir y manejar fácilmente nuestro código como si
132
fueran librerías instalables de Python. De este modo se pueden utilizar como
módulos standard desde el intérprete sin cargarlos previamente.
Para crear un paquete primero vamos a crear un nuevo directorio que tendrá
por nombre el nombre del paquete, en este ejemplo lo llamaremos
simplemente paquete. Dentro de este nuevo directorio vamos a crear un nuevo
archivo llamado _ _init__.py sin ningún contenido, el archivo vacío. Por último
vamos a copiar dentro del directorio paquete el archivo saludos.py que hicimos
anteriormente.
saludar()
Saludo()
python3 test.py
Hola, te estoy saludando desde el __init__() de la clase Saludo
Manejo de ficheros
Hasta ahora todo lo que hemos visto son pequeños programas o scripts que
funcionan almacenando información como variables, constantes u objetos en
tiempo de ejecución, es decir, que solo existen mientras el programa se está
ejecutando. Pero hemos llegado a un punto en el que probablemente nos
interese almacenar algunos de los datos con los que hemos aprendido a
trabajar en algún fichero, de modo que al cerrar o apagar el programa estos
133
queden persistentemente en un archivo que luego podría volver a cargarse al
ejecutar el programa de nuevo, de ese modo no se perdería la información.
● Creación
● Apertura o lectura
● Modificación
● Cierre
Creación
Una vez importado el paquete open de la librería io podemos comenzar con la
creación de un programa que escriba una línea de texto en un archivo, de
modo que si el archivo no existe lo cree. Para ello será necesario crear una
nuevo archivo llamado t est_ficheros.py con el siguiente código:
texto = 'Esta es una línea de texto.\nY esta es otra línea de texto.\n'
fichero.write(texto)
fichero.close()
En este ejemplo hemos creado una variable llamada texto con el las líneas de
texto que vamos a escribir en el fichero, y otra variable llamada fichero a la
que le hemos asignado como valor un objeto de tipo open() al que le hemos
pasado dos argumentos, el primero es el nombre del fichero con el que vamos
a trabajar, y el segundo argumento es la modalidad que vamos a utilizar, que
en este caso es w de escritura en inglés (write). Esta modalidad creará el
archivo si no existe y escribirá el texto dentro. Si el archivo existiera lo
sobrescribirá.
134
Si ejecutamos el programa veremos que no hay salida por pantalla alguna,
pero si listamos los archivos que se encuentran en el directorio actual
podremos ver que se ha creado un archivo nuevo llamado fichero.txt, el cual
podremos abrir para ver qué contiene, y podremos comprobar que en su
interior aparece nuestras líneas de texto de la variable t exto.
Apertura o lectura
Con esta modalidad 'r' estaríamos abriendo el fichero en modo lectura (read) .
Ahora podríamos almacenar en una variable llamada texto el contenido del
fichero, cerrar el fichero e imprimir el contenido de la variable texto de la
siguiente manera:
fichero.close()
print(texto)
python3 test_ficheros.py
Esta es una línea de texto.
Y esta es otra línea de texto.
Una manera de leer un archivo línea a línea es almacenando cada línea en una
lista. Esto se puede hacer con un método llamado readlines() que tienen los
objetos de tipo archivo, por ejemplo:
print(lineas)
fichero.close()
135
Modificación
Esta modalidad no solo sirve para añadir líneas al final del archivo, si no que
también lo crea si este no existe. Si abrimos el fichero fichero.txt veremos
que ha añadido al final de este una nueva línea con un texto.
Existe una manera un poco más óptima de leer el contenido de un fichero, esta
es mediante la sentencia w ith, veamos un ejemplo:
python3 test_ficheros.py
Esta es una línea de texto.
fichero.seek(10)
fichero.close()
print(texto)
136
python3 test_ficheros.py
a línea de texto.
Y esta es otra línea de texto.
Esta es una línea nueva.
El propio método read() que usamos para leer el contenido del fichero desde
la posición del fichero también tiene la posibilidad de recibir un argumento
para indicarle el número de carácteres que queremos leer o desplazar el
puntero, por ejemplo:
fichero.close()
print(texto)
python3 test_ficheros.py
Esta e
Existe una modalidad que nos permite leer el archivo y además escribir en él,
pero ubicando el puntero en la primera posición, esta modalidad se define
mediante ' r+' de la siguiente manera:
fichero.close()
print(texto)
fichero.seek(0)
fichero.writelines(lineas)
fichero.seek(0)
137
texto = fichero.read()
fichero.close()
print(texto)
import pickle
A continuación crearemos una lista con unos números y al igual que antes
creamos una variable llamada fichero que tendrá por valor el objeto de un
fichero abierto al que llamaremos lista.bin y lo haremos en modalidad de
escritura binaria ' wb', por ejemplo:
pickle.dump(lista, fichero)
fichero.close()
138
Ahora veremos cómo podemos hacer para leer el fichero una vez lo hemos
generado y cómo poder recuperar nuestra lista. Primero abrimos el fichero en
modo lectura binaria ('rb') y luego creamos una variable llamada lista que
tenga por valor una llamada al método load() del módulo pickle al que le
pasamos el fichero como argumento. Cerramos el fichero e imprimimos el
contenido de lista:
fichero.close()
print(lista)
python3 test_ficheros.py
[1, 2, 3, 4, 5]
Pandas
Pandas es una librería de Python creada específicamente para el análisis de
datos. Tiene un elemento clave denominada Dataframe, que no es más que
una serie de datos en una tabla donde podremos ver los registros en filas y
columnas ordenados por un índice. Cada una de las columnas puede tener un
tipo de dato diferente, por ejemplo datos de tipo entero, float, strings, objetos,
etc.
Importación de Pandas
A partir de este momento podremos utilizar el alias pd para invocar cualquier
método de la librería Pandas. Por ejemplo, para importar un fichero CSV lo
haríamos de la siguiente manera:
139
df = pd.read_csv(
r'file.csv',
index_col=0,
nrows=5,
encoding='ISO-8859-1',
delimiter=';'
)
df = pd.read_csv(
r'../datasets/Info_pais.csv',
encoding='ISO-8859-1',
delimiter=';'
)
df.head()
140
A este método head() se le puede indicar dentro de los paréntesis el número de
registros que se quieren mostrar, por ejemplo para mostrar solo los 20 primeros
registros se haría de la siguiente manera:
df.head(20)
df_order = df.sort_values(
'Esperanza de vida',
ascending=True
141
)
A partir de este momento podremos utilizar en nuestro código el alias plt para
acceder a todos los métodos de esta librería. Veamos un ejemplo en el que
cargaremos un array de datos para el eje x, llamado por ejemplo year, y otro
array de datos para el eje y, llamado por ejemplo value:
plt.plot(year, value)
142
Si quisiéramos generar otro tipo de gráfico con los mismos datos podríamos
utilizar por ejemplo el método scatter() al que también hay que pasarle como
argumentos los datos de los ejes x e y:
plt.scatter(year, value)
Esta sería una manera de crear gráficos muy sencillos a partir de un par de
listas con datos, pero normalmente se suelen utilizar fuentes de datos más
grandes y complejas como son los dataframes que hemos visto anteriormente.
Para visualizar con la librería m atplotlib la información de un dataframe
podemos hacerlo de la siguiente manera. Primero importamos el dataframe, en
este caso uno llamado h um_temp.csv con algunos datos random relativos a
humedad y temperatura:
143
df = pd.read_csv(
r'../datasets/hum_temp.csv',
encoding='ISO-8859-1',
delimiter=';'
)
df
plt.plot(df['temperature'])
144
Veamos una manera mejor de representar los datos de este dataframe, en este
caso usaremos el método plot() con el dataframe df de la siguiente manera:
df['humidity'].plot()
df.plot()
145
Ejemplo Standard & Poor's 500
Veamos otro ejemplo en el que vamos a trabajar con otro dataset. En este caso
vamos a ver la evolución de la cotización de un índice bursátil como el
Standard & Poor's 500.
df_sp500 = pd.read_csv(
r'../datasets/SP500_data.csv',
encoding='ISO-8859-1',
delimiter=','
)
df_sp500.head()
Ahora vamos a representar la columna del cierre bursátil de cada día, columna
Close:
df_sp500['Close'].plot()
146
En este gráfico podremos ver la evolución del índice bursátil, pero si nos
fijamos en el eje x ha representado el índice de cada registro. Para poder
representar en el eje x la fecha del dato debemos especificar que el índice del
dataframe d f_sp500 ha de ser la columna Date, de la siguiente forma:
df_sp500.index = df_sp500['Date']
df_sp500.head()
df_sp500['Close'].plot()
147
Ahora vemos que en el eje x se representan los valores del campo o columna
Date.
En este otro ejemplo vamos a trabajar con un dataset que he obtenido de este
site. Se tratan de los casos detectados de COVID-19 por comunidades
autónomas en España.
df_covid19_ccaas = pd.read_csv(
r'../datasets/datos_ccaas.csv',
encoding='ISO-8859-1',
delimiter=','
)
df_covid19_ccaas.head()
148
Como siempre, importamos la librería matplotlib si no lo tuviéramos importada
de ejecuciones anteriores.
df_covid19_ccaas.index = df_covid19_ccaas['fecha']
df_covid19_ccaas['num_casos'].plot()
df = pd.read_csv(
149
r'../datasets/Info_pais.csv',
encoding='ISO-8859-1',
delimiter=';'
)
df_order = df.sort_values(
'Esperanza de vida',
ascending=True
)
df_order.head()
150
De momento se aprecia un gráfico que nos da una idea aproximada de cómo
se ven los datos. Podemos añadir un título y etiquetas a los ejes x e y del
gráfico de la siguiente manera:
plt.scatter(
df_order['Renta per capita'],
df_order['Esperanza de vida']
)
plt.title('Renta per cápita vs Esperanza de vida')
plt.xlabel('Renta per cápita')
plt.ylabel('Esperanza de vida')
df_order['Poblacion_normalizada'] =
df_order['Poblacion']/max(df_order['Poblacion'])
De este modo, el país que tenga la población más alta quedaría escalado a 1 y
el resto de países quedarían normalizados en base a este valor máximo.
151
Para evitar que un país con esta gran cantidad de habitantes inunde el gráfico
es recomendable que en vez de dividir la población de cada país entre el
máximo de población, hacer la división del máximo entre 10000, para no tener
un factor tan elevado.
df_order['Poblacion_normalizada'] =
df_order['Poblacion']/(max(df_order['Poblacion'])/10000)
df_order.head()
Ahora podremos usar esta nueva columna con los datos escalados para crear
una visualización de los datos mucho más potente.
plt.scatter(
df_order['Renta per cápita'],
df_order['Esperanza de vida'],
s=df_order['Poblacion_normalizada']
)
plt.title('Renta per cápita vs Esperanza de vida')
plt.xlabel('Renta per capita')
plt.ylabel('Esperanza de vida')
152
El problema que vemos es que la visualización es muy pequeña, pero podemos
mejorar esto añadiendo a nuestro código lo siguiente para aumentar las
pulgadas de nuestro gráfico:
plt.scatter(
df_order['Renta per capita'],
df_order['Esperanza de vida'],
s=df_order['Poblacion_normalizada']
)
plt.title('Renta per cápita vs Esperanza de vida')
plt.xlabel('Renta per cápita')
plt.ylabel('Esperanza de vida')
fig = plt.gcf()
fig.set_size_inches(14.5, 10)
153
De este modo se ve mucho más grande. Si fuera necesario se pueden cambiar
los valores de la función set_size_inches() por otros más adecuados para
ajustar el tamaño.
Ahora vamos a modificar el color. Para ello debemos añadir el atributo c (color),
a continuación del atributo s (size), y como valor vamos a usar de nuevo la
columna P oblacion_normalizada. Quedaría del siguiente modo:
plt.scatter(
df_order['Renta per capita'],
df_order['Esperanza de vida'],
s=df_order['Poblacion_normalizada'],
c=df_order['Poblacion_normalizada']
)
plt.title('Renta per cápita vs Esperanza de vida')
plt.xlabel('Renta per cápita')
plt.ylabel('Esperanza de vida')
154
fig = plt.gcf()
fig.set_size_inches(14.5, 10)
También podremos añadir la etiqueta del nombre del país dentro de cada
burbuja utilizando el método annotate(), por ejemplo añadiendolo solo a los 10
primeros países con el siguiente código:
plt.scatter(
df_order['Renta per capita'],
df_order['Esperanza de vida'],
s=df_order['Poblacion_normalizada'],
c=df_order['Poblacion_normalizada']
)
plt.title('Renta per cápita vs Esperanza de vida')
plt.xlabel('Renta per cápita')
plt.ylabel('Esperanza de vida')
fig = plt.gcf()
fig.set_size_inches(14.5, 10)
155
for i in range(1, 10):
plt.annotate(
df_order['País'][i],
(
df_order['Renta per capita'][i],
df_order['Esperanza de vida'][i]
)
)
Otro aspecto que podríamos mejorar es la representación de los datos del eje
y, son los datos de 120 países y actualmente se ven muy juntos, se ve mal. esto
se soluciona fácilmente con la función y ticks() a la que le pasaremos tres
argumentos, que son un 1 representando el primer dato, 1 20 representando el
último dato, y 1 0 para indicar que los queremos mostrar de diez en diez.
plt.scatter(
df_order['Renta per capita'],
df_order['Esperanza de vida'],
s=df_order['Poblacion_normalizada'],
c=df_order['Poblacion_normalizada']
)
plt.title('Renta per cápita vs Esperanza de vida')
156
plt.xlabel('Renta per cápita')
plt.ylabel('Esperanza de vida')
fig = plt.gcf()
fig.set_size_inches(14.5, 10)
plt.yticks(ticks=range(1, 120, 1
0))
De este modo hemos creado una visualización de los datos muy potente y de
una manera muy sencilla. La conclusión que podemos sacar de este gráfico es
que efectivamente existe una correlación entre la renta per cápita y la
esperanza de vida según el país. Podemos ver que conforme la renta per
cápita aumenta la esperanza de vida también aumenta. También podemos
157
deducir que el número de población no afecta a la esperanza de vida, ya que
en la visualización que hemos creado se pueden ver países con una gran
cantidad de población que no están entre los valores más bajos en cuanto a
esperanza de vida se refiere.
Se dice que una variable es discreta cuando no puede tomar ningún valor entre
dos consecutivos, y que es continua cuando puede tomar cualquier valor dentro
de un intervalo. Por ejemplo:
● Variable discreta:
● Variable continua:
Para calcular la media primero debemos sumar todos los valores de la variable
v1 y dividirlo entre la cantidad de valores:
sum(v1)/len(v1)
158
9.6
La mediana es el valor central de los valores de una variable, una vez estos
están ordenados de manera ascendente. Veamos un ejemplo, utilizando los
valores de la variable v1 de antes, solo que con los elementos de la lista
ordenados de forma ascendente y almacenados en una nueva variable llamada
v2:
Para calcular la mediana podríamos hacer una función llamada mediana que
reciba como argumento una lista de números, en nuestro caso le pasamos la
variable v 2, por ejemplo:
def mediana(lista):
lst_sorted = sorted(lista)
lst_len = len(lista)
index = (lst_len - 1) // 2 # Floor division.
mediana(v2)
7.0
Como resultado obtenemos que la mediana de estos valores es 7, que coincide
con la posición central de los elementos de la variable v2. Si la variable tuviese
un número par de elementos, la mediana sería la suma de los dos elementos
centrales dividido entre dos.
159
Existe una diferencia entre la media y la mediana porque hay un valor que dista
mucho del resto de valores de la secuencia, en nuestro caso es el 45. A este
tipo de datos se los denomina outliers. Si por el contrario todos los valores de la
secuencia tuvieran un valor más o menos parecido podríamos ver que la media
y la media tendrían también un valor similar.
Otro ejemplo con valores en los que encontramos un outlier aún más elevado:
def mediana(lista):
lst_sorted = sorted(lista)
lst_len = len(lista)
index = (lst_len - 1) // 2 # Floor division.
mediana(v2)
160
La media de v2 es: 25.6
La mediana de v1 es: 7
La mediana de v2 es: 25
Aquí vemos que la media en ambas variables v1 y v2 es 25.6, sin embargo en
las medianas existe una enorme diferencia, debido a que el resultado se ve
más impactado por estos outliers, en este caso 282, que dista mucho del resto
de valores de la secuencia.
Veamos un caso de uso. Tenemos una variable x que tiene como valor el peso
de los tomates que hemos recolectado durante un periodo de 8 días.
161
● Tomate x_5 = 51gr
● Tomate x_6 = 53gr
● Tomate x_7 = 69gr
● Tomate x_8 = 54gr
El primer paso que hay que dar es calcular la media de la variable x, tal y como
hemos visto anteriormente:
sum(x)/len(x)
59.0
162
print(n)
1
9
4
81
64
36
100
25
sum(map(lambda i : pow(i-59, 2)
, x))
320
320/8
40.0
import math
math.sqrt(40)
6.324555320336759
Obtenemos como resultado 6.32. Esto nos permite tener un valor que se
encuentra dentro del orden de magnitud de nuestra variable x, es decir, entre
todos nuestros tomates existe una desviación estándar de 6.32 gramos.
163
● Tomate y_4 = 78gr
● Tomate y_5 = 41gr
● Tomate y_6 = 63gr
● Tomate y_7 = 59gr
● Tomate y_8 = 64gr
Vemos que hay una mayor dispersión en los datos de partida, sin embargo la
media es 59, la misma que en el caso de la variable x.
sum(y)/len(y)
59.0
Si ahora calculamos del mismo modo que antes el numerador del término de la
varianza podremos ver que en la tabla hay valores mucho más elevados que lo
que teníamos previamente:
164
25
sum(map(lambda i : pow(i-59, 2)
, y))
920
Esto se debe a que la secuencia original de datos dista mucho mas del valor
promedio de 59 gramos. Con estos nuevos datos, si calculamos la varianza en
este caso obtendremos un valor de 115.
920/8
115.0
import math
math.sqrt(115)
10.723805294763608
Lo que hemos aprendido con estos dos ejemplos es que conforme el conjunto
de valores tiene una mayor dispersión respecto al valor promedio, al final
tendrá un valor de varianza superior. Por lo tanto la varianza nos proporciona
una medida de la volatilidad o incertidumbre de una determinada variable o
conjunto de datos. De hecho, si todos los valores de la variable fueran iguales
el valor de la varianza sería 0 , es decir, no habría ningún tipo de incertidumbre.
Se suele utilizar más la varianza puesto que está menos influenciada por los
valores positivos o negativos que pudiera haber en la diferencia entre el valor y
el promedio, por lo tanto la varianza es más representativa a la hora de calcular
la dispersión de los datos de nuestro conjunto de datos.
NumPy
NumPy es una librería de Python enfocada en el cálculo numérico que nos
permite realizar operaciones de una manera sencilla y rápida. Su objeto base
es un vector de números denominado Array. Es una alternativa a las listas que
hemos visto hasta ahora y nos va a permitir realizar una serie de funciones muy
165
potentes. A diferencia de las listas, donde se opera de forma independiente en
cada uno de los elementos, en los Arrays las operaciones se van a realizar
sobre todo el Array simultáneamente. A continuación se muestra una diferencia
entre listas y Arrays de NumPy a la hora de realizar una suma de elementos,
por ejemplo, tenemos estas dos listas:
a = [1, 2
, 3 ]
b = [4, 5 , 6 ]
a + b
[1, 2, 3, 4, 5, 6]
a = np.array([1, 2
, 3 ])
b = np.array([4, 5 , 6 ])
a + b
array([5, 7, 9])
En este caso se han sumado cada uno de los elementos del Array a con los
elementos del array b.
Otra gran diferencia respecto a las listas tradicionales es que en un Array solo
se admite un tipo de dato, normalmente numérico. Además NumPy nos va a
servir como base de cálculo para otras librerías como Pandas o SciKit Learn.
Importación de NumPy
166
Ejemplo Índice de Masa Corporal (IMC)
En este ejemplo vamos a calcular el índice de masa corporal sobre los valores
ltura de tres personas.
peso y a
El cálculo que hay que hacer para obtener el IMC es dividir el peso entre el
cuadrado de la altura:
peso / altura**2
El cálculo está bien hecho, pero de este modo es más complicado, entre otras
cosas porque el cálculo de va haciendo secuencialmente elemento a elemento
entre las listas, y en este caso no es mucho problema ya que son listas de solo
3 elementos, pero podría ser un problema al trabajar con listas de miles de
elementos. Se realizar el mismo cálculo de una manera mucho más eficiente
usando los Arrays de NumPy, veamos el ejemplo:
Además este tipo de objetos Array de NumPy son iterables del mismo modo
que lo son algunas colecciones estándar de Python como las listas, tienen
algunas propiedades como los slices que nos permiten navegar dentro del
Array y acceder a determinados elementos ubicados en ciertas posiciones.
Otro uso interesante de los Arrays es que podemos evaluar todos los
elementos del Array con una simple operación, como por ejemplo saber qué
valores son mayores que 21:
167
imc > 21
array([ True, False, True])
En este caso el primer resultado es True puesto que se cumple que 23.183391
es mayor que 21, el segundo es False porque 20.2020202 no es mayor que 21 y
el tercero es True puesto que 21.73650525 si es mayor que 21.
Si quisiéramos obtener solo los elementos del array que cumplen la condición
anterior podremos hacerlo de la siguiente manera:
base * altura / 2
Como solo nos interesan las áreas que son > 6.5 podremos obtener los valores
que cumplan la condición de la siguiente manera:
168
También podemos hacer una conjunción de condiciones, por ejemplo para
obtener solo las áreas que son > 6.5 y también < 8, para ello usaremos el AND
lógico mediante el símbolo & de la siguiente manera:
nombre_array = np.array([[valores_fila_1],
[valores_fila_2],
[valores_fila_m]])
169
teniendo en cuenta que los índices siempre empiezan por el número 0. En este
ejemplo el valor 10 se encuentra en la fila con índice 1 y la columna con índice
2.
a[1, 2]
10
Ahora supongamos que queremos obtener todas las filas pero solo los valores
de las columnas primera y segunda. En este caso tendríamos que especificar
en primer lugar que queremos todas las filas mediante los dos puntos :, y a
continuación un slice 0 :2 para indicar solo las columnas desde el índice 0 hasta
el 1 , ya que en un slice el número que se indica al final no se muestra, es
donde se para.
a[:, 0:2]
array([[2, 7 ],
[4, 8 ]])
Para realizar cálculo estadístico NumPy nos ofrece una gran variedad de
funciones muy útiles y potentes. En esta sección veremos algunas de ellas que
nos pueden servir para solucionar algunos cálculos que hemos visto en puntos
anteriores de una manera mucho más sencilla y rápida.
Por ejemplo, supongamos que tenemos el siguiente Array con varios registros
de temperaturas de una ciudad:
np.mean(temperaturas)
15.050526315789472
np.median(temperaturas)
15.0
170
Podremos obtener los valores mínimos y máximos con las funciones min(array)
y max(array).
np.min(temperaturas)
12.0
np.max(temperaturas)
17.5
np.var(temperaturas)
2.2893207756232683
np.std(temperaturas)
1.5130501563475245
np.percentile(temperaturas, 90)
17.04
Una cualidad de NumPy muy interesante es que nos permite generar datos
random tomando como partida diferentes parámetros estadísticos, como por
ejemplo una media y una desviación estándar, y generar un Array de valores
con dicha distribución estadística. La función es random.normal() necesita que le
pasemos como argumentos una media, la desviación estándar y un número de
muestras. Su sintaxis es la siguiente:
nombre_array = np.random.normal(
171
media,
desviacion_estandar,
numero_muestras
)
media = np.mean(array_gauss)
media
2.00020348250122
desviacion = np.std(array_gauss)
desviacion
0.496490256867947
plt.scatter(
array_gauss,
stats.norm.pdf(array_gauss, media, desviacion)
)
172