Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Fecha: 01/11/2022
Curso: Udemy - Curso Maestro de Python
Profesor: Héctor Costa Guzmán
Trabajando con números
Utilizando el intérprete como una calculadora
3+2
3-2
3*2
# División
3/2
1.5
# Módulo
3%2
# Potencia
3**2
323239829389.238273283
323239829389.2383
3-2+4*10
41
Las variables
Concepto fundamental de la programación, en el cuál se define un identificador y se le asigna un valor.
Luego podemos utilizarlas como si se tratase de un valor literal, incluso operarlas entre otras variables y volver a asignarles un valor en
cualquier momento.
n+3
n*2
n*2
n*n
m=10
n+m
13
n*m+10
40
n=10
m=15
n+m
25
n=m
15
15
n=m+10
25
n=n+25
50
Reutilización
Al crear una estructura de cálculos con variables, podemos fácilmente adaptar sus valores para hacer distintas comprobaciones.
nota_1 = 2
nota_2 = 5
nota_media = (nota_1 + nota_2) / 2
nota_media
3.5
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Textos (cadenas de caracteres)
'Hola Mundo'
'Hola Mundo'
"Hola Mundo"
'Hola Mundo'
La función print()
Es una instrucción que nos permite mostrar correctamente el valor de una cadena (u otros valores/variables) por pantalla.
"Una cadena"
'otra cadena'
'otra cadena más'
print("Una cadena")
print('otra cadena')
print('otra cadena más')
Una cadena
otra cadena
otra cadena más
Un texto
una nueva línea
Para evitar los carácteres especiales, debemos indicar que una cadena es cruda (raw)
print("C:\nombre\directorio")
C:
ombre\directorio
C:\nombre\directorio
print("""Una línea
otra línea
otra línea\tuna tabulación""")
Una línea
otra línea
otra línea una tabulación
print(c)
Operaciones
Una de las operaciones de las cadenas es la concatenación (o suma de cadenas)
c + c
print(c+c)
c1 = "Una cadena"
c2 = "otra cadena"
print("Una cadena " + c2)
Representan un número [índice], que empezando por el 0 indica el carácter de la primera posición, y así sucesivamente.
palabra = "Python"
'P'
palabra[3]
'h'
El índice negativo -1, hace referencia al carácter de la última posición, el -2 al penúltimo y así sucesivamente
palabra[-1]
'n'
palabra[-0]
'P'
palabra[-2]
'o'
palabra[-6]
'P'
palabra[5]
'n'
palabra = "Python"
palabra[0:2]
'Py'
palabra[2:]
'thon'
palabra[:2]
'Py'
palabra[:]
'Python'
palabra[:2] + palabra[2:]
'Python'
palabra[-2:]
'on'
palabra[99]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-47-b31ddef6ab27> in <module>()
----> 1 palabra[99]
palabra[:99]
'Python'
palabra[99:]
''
Inmutabilidad
Una propiedad de las cadenas es que no se pueden modificar. Si intentamos reasignar un carácter, no nos dejará:
palabra[0] = "N"
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-51-c87a9e773639> in <module>()
----> 1 palabra[0] = "N"
Sin embargo, utilizando slicing y concatenación podemos generar nuevas cadenas fácilmente:
'Nython'
Funciones
Un ejemplo de función útil que soportan las cadenas es len(), que nos permite saber su longitud (el número de caracteres que
contienen).
len(palabra)
Hay más funciones, pero las iremos descubriendo a lo largo del curso.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Las listas
Tipo compuesto de dato que puede almacenar distintos valores (llamados ítems) ordenados entre [ ] y separados con comas.
numeros = [1,2,3,4]
Índices y slicing
Funcionan de una forma muy similar a las cadenas de caracteres.
datos[0]
datos[-1]
'Otra cadena'
datos[-2:]
Suma de listas
Da como resultado una nueva lista que incluye todos los ítems.
numeros + [5,6,7,8]
[1, 2, 3, 4, 5, 6, 7, 8]
Son modificables
A diferencia de las cadenas, en las listas sí podemos modificar sus ítems utilizando índices:
pares = [0,2,4,5,8,10]
pares[3]= 6
pares
[0, 2, 4, 6, 8, 10]
Integran funcionalidades internas, como el método .append() para añadir un ítem al final de la lista
pares.append(12)
pares
pares.append(7*2)
pares
Y una peculiaridad, es que también aceptan asignación con slicing para modificar varios ítems en conjunto
letras = ['a','b','c','d','e','f']
letras[:3]
letras[:3] = ['A','B','C']
letras
letras
Asignar una lista vacía equivale a borrar los ítems de la lista o sublista
letras[:3] = []
letras
letras = []
letras
[]
La función len() también funciona con las listas del mismo modo que en las cadenas:
len(letras)
len(pares)
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
r = [a,b,c]
[1, 2, 3]
[7, 8, 9]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Leyendo valores por teclado
Se consigue utilizando la instrucción input() que lee y devuelve una cadena:
valor = input()
algo
valor
'algo'
valor = input()
100
'100'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-5071d551e583> in <module>()
----> 1 valor + 100
valor
500
1500
10 + valor
20.5
valor
10.5
valor
3.14
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ejemplo de cabecera
Intenta deducir qué hace este programa (no hace falta que le des muchas vueltas).
n = 0
while n < 10: # while = mientras
if (n % 2) == 0: # if = si (de condicion)
print(n,'es un número par')
else: # else = sino (def condicion)
print(n,'es un número impar')
n = n + 1
0 es un número par
1 es un número impar
2 es un número par
3 es un número impar
4 es un número par
5 es un número impar
6 es un número par
7 es un número impar
8 es un número par
9 es un número impar
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 01: Introducción informal (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Identifica el tipo de dato (int, float, string o list) de los siguientes valores literales
2) Determina mentalmente (sin programar) el resultado que aparecerá por pantalla a partir de las siguientes variables:
a = 10
b = -5
c = "Hola "
d = [1, 2, 3]
print(a * 5) # 50
print(a - b) # 15
print(c + "Mundo") # Hola Mundo
print(c * 2) # Hola Hola
print(c[-1]) # 3
print(c[1:]) # [2, 3]
print(d + d) # [1, 2, 3, 1, 2, 3]
3) El siguiente código pretende realizar una media entre 3 números, pero no funciona correctamente. ¿Eres capaz de identificar
el problema y solucionarlo?
numero_1 = 9
numero_2 = 3
numero_3 = 6
media = (numero_1 + numero_2 + numero_3) / 3 # Hay que realizar primero la suma de los 3 números antes de dividir
print("La nota media es", media)
4) A partir del ejercicio anterior, vamos a suponer que cada número es una nota, y lo que queremos es obtener la nota final. El
problema es que cada nota tiene un valor porcentual:
nota_1 = 10
nota_2 = 7
nota_3 = 4
5) La siguiente matriz (o lista con listas anidadas) debe cumplir una condición, y es que en cada fila, el cuarto elemento
siempre debe ser el resultado de sumar los tres primeros. ¿Eres capaz de modificar las sumas incorrectas utilizando la técnica
del slicing?
Ayuda: La función llamada sum(lista) devuelve una suma de todos los elementos de la lista ¡Pruébalo!
matriz = [
[1, 1, 1, 3],
[2, 2, 2, 7],
[3, 3, 3, 9],
[4, 4, 4, 13]
]
print(matriz)
[[1, 1, 1, 3], [2, 2, 2, 6], [3, 3, 3, 9], [4, 4, 4, 12]]
6) Al realizar una consulta en un registro hemos obtenido una cadena de texto corrupta al revés. Al parecer contiene el nombre
de un alumno y la nota de un exámen. ¿Cómo podríamos formatear la cadena y conseguir una estructura como la siguiente?:
Ayuda: Para voltear una cadena rápidamente utilizando slicing podemos utilizar un tercer índice -1: cadena[::-1]
Verdadero (True)
Falso (False)
1+1 == 3
False
1+1 == 2
True
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Los operadores relacionales
Sirven para comparar dos valores, dependiendo del resultado de la comparación pueden devolver:
# Igual que
3 == 2
False
# Distinto de
3 != 2
True
# Mayor que
3 > 2
True
# Menor que
3 < 2
False
False
True
a = 10
b = 5
a > b
True
b != a
True
a == b*2
True
Y otros tipos, como cadenas, listas, el resultado de algunas funciones o los propios tipos lógicos
"Hola" == "Hola"
True
"Hola" != "Hola"
False
c = "Hola"
c[0] == "H"
True
c[-1] == "a"
True
l1 = [0,1,2]
l2 = [2,3,4]
l1 == l2
False
len(l1) == len(l2)
True
l1[-1] == l2[0]
True
True == True
True
False == True
False
False != True
True
True
False
True * 3
False / 5
0.0
True * False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Operadores lógicos
Encontramos 3 básicos:
Not
And
Or
False
True
True
False
False
False
a = 45
a > 10 and a < 20
False
c = "Hola Mundo"
len(c) >= 20 and c[0] == "H"
False
True
True or False
True
False or True
True
False or False
False
c = "OTRA COSA"
c == "EXIT" or c == "FIN" or c == "SALIR"
False
c = "Lector"
c[0] == "H" or c[0] == "h"
False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Expresiones anidadas
Se pueden solucionar empleando las reglas de precedencia:
a = 10
b = 5
a * b - 2**b >= 20 and not (a % b) != 0
False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Operadores de asignación
Actúan directamente sobre la variable actual modificando su valor.
a = 0
a += 5 # suma en asignación
a
10
a -= 10 # resta en asignación
a
a = 5
a *= 2 # producto en asignación
a
10
a /= 2 # división en asignación
a
5.0
a %= 2 # módulo en asignación
1.0
1.0
a = 5
a **= 5
a
3125
n = 0 # Asignación de 0 en n
while n < 10: # Expresión relacional n < 10, que devuelve True
if (n % 2) == 0: # Expresión aritmética y expresión relacional
print(n,'es un número par')
else:
print(n,'es un número impar')
n += 1 # expresión aritmética n = n + 1 equivalente a operación en asignación n+=1
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 02: Operadores y expresiones (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Realiza un programa que lea 2 números por teclado y determine los siguientes aspectos (es suficiene con mostrar True o
False):
2) Utilizando operadores lógicos, determina si una cadena de texto introducida por el usuario tiene una longitud mayor o igual
que 3 y a su vez es menor que 10 (es suficiene con mostrar True o False):
3) Realiza un programa que cumpla el siguiente algoritmo utilizando siempre que sea posible operadores en asignación:
Se cumple la condición
También se muestre este print
a = 5
if a == 2:
print("a vale 2")
if a == 5:
print("a vale 5")
a vale 5
a = 5
b = 10
if a == 5:
print("a vale",a)
if b == 10:
print("y b vale",b)
a vale 5
y b vale 10
Como condición podemos evaluar múltiples expresiones, siempre que éstas devuelvan True o False
a vale 5 y b vale 10
n = 11
if n % 2 == 0:
print(n,"es un número par")
else:
print(n,"es un número impar")
11 es un número impar
Instrucción Pass
Sirve para finalizar un bloque, se puede utilizar en un bloque vacío.
if True:
pass
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Condiciones
Sentencia If (Si)
Permite dividir el flujo de un programa en diferentes caminos. El if se ejecuta siempre que la expresión que comprueba devuelva True
Se cumple la condición
También se muestre este print
a = 5
if a == 2:
print("a vale 2")
if a == 5:
print("a vale 5")
a vale 5
a = 5
b = 10
if a == 5:
print("a vale",a)
if b == 10:
print("y b vale",b)
a vale 5
y b vale 10
Como condición podemos evaluar múltiples expresiones, siempre que éstas devuelvan True o False
a vale 5 y b vale 10
n = 11
if n % 2 == 0:
print(n,"es un número par")
else:
print(n,"es un número impar")
11 es un número impar
Instrucción Pass
Sirve para finalizar un bloque, se puede utilizar en un bloque vacío.
if True:
pass
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Iteraciones
Iterar significa realizar una acción varias veces. Cada vez que se repite se denomina iteración.
Queda en las manos del programador decidir el momento en que la condición cambie a False para hacer que el While finalice.
c = 0
while c <= 5:
c+=1
print("c vale",c)
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
c = 0
while c <= 5:
c+=1
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
Se ha completado toda la iteración y c vale 6
Instrucción Break
Sirve para "romper" la ejecución del While en cualquier momento. No se ejecutará el Else, ya que éste sólo se llama al finalizar la
iteración.
c = 0
while c <= 5:
c+=1
if (c==4):
print("Rompemos el bucle cuando c vale",c)
break
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 3
Rompemos el bucle cuando c vale 4
Instrucción Continue
Sirve para "saltarse" la iteración actual sin romper el bucle.
c = 0
while c <= 5:
c+=1
if c==3 or c==4:
# print("Continuamos con la siguiente iteración",c)
continue
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 5
c vale 6
Se ha completado toda la iteración y c vale 6
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
numeros
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Sin embargo, ésto no funciona. La forma correcta de hacerlo es haciendo referencia al índice de la lista en lugar
de la variable:
indice = 0
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for numero in numeros:
numeros[indice] *= 10
indice+=1
numeros
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Podemos utilizar la función enumerate() para conseguir el índice y el valor en cada iteración fácilmente:
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
a
m
i
g
o
s
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-8ba888c46579> in <module>()
1 for i,c in enumerate(cadena):
----> 2 cadena[i] = "*"
cadena2 = ""
for caracter in cadena:
cadena2 += caracter * 2
cadena
'Hola amigos'
cadena2
'HHoollaa aammiiggooss'
La función range()
Sirve para generar una lista de números que podemos recorrer fácilmente, pero no ocupa memoria porque se interpreta sobre la
marcha:
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
range(10)
range(0, 10)
for i in [0,1,2,3,4,5,6,7,9]:
print(i)
0
1
2
3
4
5
6
7
9
list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ejemplo de cabecera
n = 0
while n < 10:
if (n % 2) == 0:
print(n,'es un número par')
else:
print(n,'es un número impar')
n = n + 1
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 03: Control de flujo (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Realiza un programa que lea dos números por teclado y permita elegir entre 3 opciones en un menú:
print("¿Qué quieres hacer? \n1) Sumar los dos números\n2) Restar los dos números\n3) Multiplicar los dos números"
opcion = int(input("Introduce un número: ") )
if opcion == 1:
print("La suma de",n1,"+",n2,"es",n1+n2)
elif opcion == 2:
print("La resta de",n1,"-",n2,"es",n1-n2)
elif opcion == 3:
print("El producto de",n1,"*",n2,"es",n1*n2)
else:
print("Opción incorrecta")
Introduce un número: 5
Introduce otro número: 5
¿Qué quieres hacer?
1) Sumar los dos números
2) Restar los dos números
3) Multiplicar los dos números
Introduce un número: 3
El producto de 5.0 * 5.0 es 25.0
2) Realiza un programa que lea un número impar por teclado. Si el usuario no introduce un número impar, debe repetise el
proceso hasta que lo introduzca correctamente.
3) Realiza un programa que sume todos los números pares desde el 0 hasta el 100:
Sugerencia: Puedes utilizar la funciones sum() y range() para hacerlo más fácil. El tercer parámetro en la función range(inicio, fin, salto)
indica un salto de números, pruébalo.
print(suma)
2550
2550
4) Realiza un programa que pida al usuario cuantos números quiere introducir. Luego lee todos los números y realiza una
media aritmética:
5) Realiza un programa que pida al usuario un número entero del 0 al 9, y que mientras el número no sea correcto se repita el
proceso. Luego debe comprobar si el número se encuentra en la lista de números y notificarlo:
Consejo: La sintaxis "valor in lista" permite comprobar fácilmente si un valor se encuentra en una lista (devuelve True o False)
while True:
numero = int(input("Escribe un número del 0 al 9: "))
if numero >= 0 and numero <= 9:
break
if numero in numeros:
print("El número",numero,"se encuentra en la lista",numeros)
else:
print("El número",numero,"no se encuentra en la lista",numeros)
6) Utilizando la función range() y la conversión a listas genera las siguientes listas dinámicamente:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[-19, -17, -15, -13, -11, -9, -7, -5, -3, -1]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
7) Dadas dos listas, debes generar una tercera con todos los elementos que se repitan en ellas, pero no debe repetise ningún
elemento en la nueva lista:
lista_3 = []
print(lista_3)
3-2
3*2
# División
3/2
1.5
# Módulo
3%2
# Potencia
3**2
323239829389.238273283
323239829389.2383
3-2+4*10
41
Las variables
Concepto fundamental de la programación, en el cuál se define un identificador y se le asigna un valor.
Luego podemos utilizarlas como si se tratase de un valor literal, incluso operarlas entre otras variables y volver a asignarles un valor en
cualquier momento.
n+3
n*2
n*2
n*n
m=10
n+m
13
n*m+10
40
n=10
m=15
n+m
25
n=m
15
15
n=m+10
25
n=n+25
50
Reutilización
Al crear una estructura de cálculos con variables, podemos fácilmente adaptar sus valores para hacer distintas comprobaciones.
nota_1 = 2
nota_2 = 5
nota_media = (nota_1 + nota_2) / 2
nota_media
3.5
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Textos (cadenas de caracteres)
'Hola Mundo'
'Hola Mundo'
"Hola Mundo"
'Hola Mundo'
La función print()
Es una instrucción que nos permite mostrar correctamente el valor de una cadena (u otros valores/variables) por pantalla.
"Una cadena"
'otra cadena'
'otra cadena más'
print("Una cadena")
print('otra cadena')
print('otra cadena más')
Una cadena
otra cadena
otra cadena más
Un texto
una nueva línea
Para evitar los carácteres especiales, debemos indicar que una cadena es cruda (raw)
print("C:\nombre\directorio")
C:
ombre\directorio
C:\nombre\directorio
print("""Una línea
otra línea
otra línea\tuna tabulación""")
Una línea
otra línea
otra línea una tabulación
print(c)
Operaciones
Una de las operaciones de las cadenas es la concatenación (o suma de cadenas)
c + c
print(c+c)
c1 = "Una cadena"
c2 = "otra cadena"
print("Una cadena " + c2)
Representan un número [índice], que empezando por el 0 indica el carácter de la primera posición, y así sucesivamente.
palabra = "Python"
'P'
palabra[3]
'h'
El índice negativo -1, hace referencia al carácter de la última posición, el -2 al penúltimo y así sucesivamente
palabra[-1]
'n'
palabra[-0]
'P'
palabra[-2]
'o'
palabra[-6]
'P'
palabra[5]
'n'
palabra = "Python"
palabra[0:2]
'Py'
palabra[2:]
'thon'
palabra[:2]
'Py'
palabra[:]
'Python'
palabra[:2] + palabra[2:]
'Python'
palabra[-2:]
'on'
palabra[99]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-47-b31ddef6ab27> in <module>()
----> 1 palabra[99]
palabra[:99]
'Python'
palabra[99:]
''
Inmutabilidad
Una propiedad de las cadenas es que no se pueden modificar. Si intentamos reasignar un carácter, no nos dejará:
palabra[0] = "N"
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-51-c87a9e773639> in <module>()
----> 1 palabra[0] = "N"
Sin embargo, utilizando slicing y concatenación podemos generar nuevas cadenas fácilmente:
'Nython'
Funciones
Un ejemplo de función útil que soportan las cadenas es len(), que nos permite saber su longitud (el número de caracteres que
contienen).
len(palabra)
Hay más funciones, pero las iremos descubriendo a lo largo del curso.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Las listas
Tipo compuesto de dato que puede almacenar distintos valores (llamados ítems) ordenados entre [ ] y separados con comas.
numeros = [1,2,3,4]
Índices y slicing
Funcionan de una forma muy similar a las cadenas de caracteres.
datos[0]
datos[-1]
'Otra cadena'
datos[-2:]
Suma de listas
Da como resultado una nueva lista que incluye todos los ítems.
numeros + [5,6,7,8]
[1, 2, 3, 4, 5, 6, 7, 8]
Son modificables
A diferencia de las cadenas, en las listas sí podemos modificar sus ítems utilizando índices:
pares = [0,2,4,5,8,10]
pares[3]= 6
pares
[0, 2, 4, 6, 8, 10]
Integran funcionalidades internas, como el método .append() para añadir un ítem al final de la lista
pares.append(12)
pares
pares.append(7*2)
pares
Y una peculiaridad, es que también aceptan asignación con slicing para modificar varios ítems en conjunto
letras = ['a','b','c','d','e','f']
letras[:3]
letras[:3] = ['A','B','C']
letras
letras
Asignar una lista vacía equivale a borrar los ítems de la lista o sublista
letras[:3] = []
letras
letras = []
letras
[]
La función len() también funciona con las listas del mismo modo que en las cadenas:
len(letras)
len(pares)
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
r = [a,b,c]
[1, 2, 3]
[7, 8, 9]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Leyendo valores por teclado
Se consigue utilizando la instrucción input() que lee y devuelve una cadena:
valor = input()
algo
valor
'algo'
valor = input()
100
'100'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-5071d551e583> in <module>()
----> 1 valor + 100
valor
500
1500
10 + valor
20.5
valor
10.5
valor
3.14
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ejemplo de cabecera
Intenta deducir qué hace este programa (no hace falta que le des muchas vueltas).
n = 0
while n < 10: # while = mientras
if (n % 2) == 0: # if = si (de condicion)
print(n,'es un número par')
else: # else = sino (def condicion)
print(n,'es un número impar')
n = n + 1
0 es un número par
1 es un número impar
2 es un número par
3 es un número impar
4 es un número par
5 es un número impar
6 es un número par
7 es un número impar
8 es un número par
9 es un número impar
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 01: Introducción informal (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Identifica el tipo de dato (int, float, string o list) de los siguientes valores literales
2) Determina mentalmente (sin programar) el resultado que aparecerá por pantalla a partir de las siguientes variables:
a = 10
b = -5
c = "Hola "
d = [1, 2, 3]
print(a * 5) # 50
print(a - b) # 15
print(c + "Mundo") # Hola Mundo
print(c * 2) # Hola Hola
print(c[-1]) # 3
print(c[1:]) # [2, 3]
print(d + d) # [1, 2, 3, 1, 2, 3]
3) El siguiente código pretende realizar una media entre 3 números, pero no funciona correctamente. ¿Eres capaz de identificar
el problema y solucionarlo?
numero_1 = 9
numero_2 = 3
numero_3 = 6
media = (numero_1 + numero_2 + numero_3) / 3 # Hay que realizar primero la suma de los 3 números antes de dividir
print("La nota media es", media)
4) A partir del ejercicio anterior, vamos a suponer que cada número es una nota, y lo que queremos es obtener la nota final. El
problema es que cada nota tiene un valor porcentual:
nota_1 = 10
nota_2 = 7
nota_3 = 4
5) La siguiente matriz (o lista con listas anidadas) debe cumplir una condición, y es que en cada fila, el cuarto elemento
siempre debe ser el resultado de sumar los tres primeros. ¿Eres capaz de modificar las sumas incorrectas utilizando la técnica
del slicing?
Ayuda: La función llamada sum(lista) devuelve una suma de todos los elementos de la lista ¡Pruébalo!
matriz = [
[1, 1, 1, 3],
[2, 2, 2, 7],
[3, 3, 3, 9],
[4, 4, 4, 13]
]
print(matriz)
[[1, 1, 1, 3], [2, 2, 2, 6], [3, 3, 3, 9], [4, 4, 4, 12]]
6) Al realizar una consulta en un registro hemos obtenido una cadena de texto corrupta al revés. Al parecer contiene el nombre
de un alumno y la nota de un exámen. ¿Cómo podríamos formatear la cadena y conseguir una estructura como la siguiente?:
Ayuda: Para voltear una cadena rápidamente utilizando slicing podemos utilizar un tercer índice -1: cadena[::-1]
Verdadero (True)
Falso (False)
1+1 == 3
False
1+1 == 2
True
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Los operadores relacionales
Sirven para comparar dos valores, dependiendo del resultado de la comparación pueden devolver:
# Igual que
3 == 2
False
# Distinto de
3 != 2
True
# Mayor que
3 > 2
True
# Menor que
3 < 2
False
False
True
a = 10
b = 5
a > b
True
b != a
True
a == b*2
True
Y otros tipos, como cadenas, listas, el resultado de algunas funciones o los propios tipos lógicos
"Hola" == "Hola"
True
"Hola" != "Hola"
False
c = "Hola"
c[0] == "H"
True
c[-1] == "a"
True
l1 = [0,1,2]
l2 = [2,3,4]
l1 == l2
False
len(l1) == len(l2)
True
l1[-1] == l2[0]
True
True == True
True
False == True
False
False != True
True
True
False
True * 3
False / 5
0.0
True * False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Operadores lógicos
Encontramos 3 básicos:
Not
And
Or
False
True
True
False
False
False
a = 45
a > 10 and a < 20
False
c = "Hola Mundo"
len(c) >= 20 and c[0] == "H"
False
True
True or False
True
False or True
True
False or False
False
c = "OTRA COSA"
c == "EXIT" or c == "FIN" or c == "SALIR"
False
c = "Lector"
c[0] == "H" or c[0] == "h"
False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Expresiones anidadas
Se pueden solucionar empleando las reglas de precedencia:
a = 10
b = 5
a * b - 2**b >= 20 and not (a % b) != 0
False
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Operadores de asignación
Actúan directamente sobre la variable actual modificando su valor.
a = 0
a += 5 # suma en asignación
a
10
a -= 10 # resta en asignación
a
a = 5
a *= 2 # producto en asignación
a
10
a /= 2 # división en asignación
a
5.0
a %= 2 # módulo en asignación
1.0
1.0
a = 5
a **= 5
a
3125
n = 0 # Asignación de 0 en n
while n < 10: # Expresión relacional n < 10, que devuelve True
if (n % 2) == 0: # Expresión aritmética y expresión relacional
print(n,'es un número par')
else:
print(n,'es un número impar')
n += 1 # expresión aritmética n = n + 1 equivalente a operación en asignación n+=1
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 02: Operadores y expresiones (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Realiza un programa que lea 2 números por teclado y determine los siguientes aspectos (es suficiene con mostrar True o
False):
2) Utilizando operadores lógicos, determina si una cadena de texto introducida por el usuario tiene una longitud mayor o igual
que 3 y a su vez es menor que 10 (es suficiene con mostrar True o False):
3) Realiza un programa que cumpla el siguiente algoritmo utilizando siempre que sea posible operadores en asignación:
Se cumple la condición
También se muestre este print
a = 5
if a == 2:
print("a vale 2")
if a == 5:
print("a vale 5")
a vale 5
a = 5
b = 10
if a == 5:
print("a vale",a)
if b == 10:
print("y b vale",b)
a vale 5
y b vale 10
Como condición podemos evaluar múltiples expresiones, siempre que éstas devuelvan True o False
a vale 5 y b vale 10
n = 11
if n % 2 == 0:
print(n,"es un número par")
else:
print(n,"es un número impar")
11 es un número impar
Instrucción Pass
Sirve para finalizar un bloque, se puede utilizar en un bloque vacío.
if True:
pass
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Condiciones
Sentencia If (Si)
Permite dividir el flujo de un programa en diferentes caminos. El if se ejecuta siempre que la expresión que comprueba devuelva True
Se cumple la condición
También se muestre este print
a = 5
if a == 2:
print("a vale 2")
if a == 5:
print("a vale 5")
a vale 5
a = 5
b = 10
if a == 5:
print("a vale",a)
if b == 10:
print("y b vale",b)
a vale 5
y b vale 10
Como condición podemos evaluar múltiples expresiones, siempre que éstas devuelvan True o False
a vale 5 y b vale 10
n = 11
if n % 2 == 0:
print(n,"es un número par")
else:
print(n,"es un número impar")
11 es un número impar
Instrucción Pass
Sirve para finalizar un bloque, se puede utilizar en un bloque vacío.
if True:
pass
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Iteraciones
Iterar significa realizar una acción varias veces. Cada vez que se repite se denomina iteración.
Queda en las manos del programador decidir el momento en que la condición cambie a False para hacer que el While finalice.
c = 0
while c <= 5:
c+=1
print("c vale",c)
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
c = 0
while c <= 5:
c+=1
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 3
c vale 4
c vale 5
c vale 6
Se ha completado toda la iteración y c vale 6
Instrucción Break
Sirve para "romper" la ejecución del While en cualquier momento. No se ejecutará el Else, ya que éste sólo se llama al finalizar la
iteración.
c = 0
while c <= 5:
c+=1
if (c==4):
print("Rompemos el bucle cuando c vale",c)
break
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 3
Rompemos el bucle cuando c vale 4
Instrucción Continue
Sirve para "saltarse" la iteración actual sin romper el bucle.
c = 0
while c <= 5:
c+=1
if c==3 or c==4:
# print("Continuamos con la siguiente iteración",c)
continue
print("c vale",c)
else:
print("Se ha completado toda la iteración y c vale",c)
c vale 1
c vale 2
c vale 5
c vale 6
Se ha completado toda la iteración y c vale 6
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
numeros
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Sin embargo, ésto no funciona. La forma correcta de hacerlo es haciendo referencia al índice de la lista en lugar
de la variable:
indice = 0
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for numero in numeros:
numeros[indice] *= 10
indice+=1
numeros
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Podemos utilizar la función enumerate() para conseguir el índice y el valor en cada iteración fácilmente:
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
a
m
i
g
o
s
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-8ba888c46579> in <module>()
1 for i,c in enumerate(cadena):
----> 2 cadena[i] = "*"
cadena2 = ""
for caracter in cadena:
cadena2 += caracter * 2
cadena
'Hola amigos'
cadena2
'HHoollaa aammiiggooss'
La función range()
Sirve para generar una lista de números que podemos recorrer fácilmente, pero no ocupa memoria porque se interpreta sobre la
marcha:
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
range(10)
range(0, 10)
for i in [0,1,2,3,4,5,6,7,9]:
print(i)
0
1
2
3
4
5
6
7
9
list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ejemplo de cabecera
n = 0
while n < 10:
if (n % 2) == 0:
print(n,'es un número par')
else:
print(n,'es un número impar')
n = n + 1
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 03: Control de flujo (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
1) Realiza un programa que lea dos números por teclado y permita elegir entre 3 opciones en un menú:
print("¿Qué quieres hacer? \n1) Sumar los dos números\n2) Restar los dos números\n3) Multiplicar los dos números"
opcion = int(input("Introduce un número: ") )
if opcion == 1:
print("La suma de",n1,"+",n2,"es",n1+n2)
elif opcion == 2:
print("La resta de",n1,"-",n2,"es",n1-n2)
elif opcion == 3:
print("El producto de",n1,"*",n2,"es",n1*n2)
else:
print("Opción incorrecta")
Introduce un número: 5
Introduce otro número: 5
¿Qué quieres hacer?
1) Sumar los dos números
2) Restar los dos números
3) Multiplicar los dos números
Introduce un número: 3
El producto de 5.0 * 5.0 es 25.0
2) Realiza un programa que lea un número impar por teclado. Si el usuario no introduce un número impar, debe repetise el
proceso hasta que lo introduzca correctamente.
3) Realiza un programa que sume todos los números pares desde el 0 hasta el 100:
Sugerencia: Puedes utilizar la funciones sum() y range() para hacerlo más fácil. El tercer parámetro en la función range(inicio, fin, salto)
indica un salto de números, pruébalo.
print(suma)
2550
2550
4) Realiza un programa que pida al usuario cuantos números quiere introducir. Luego lee todos los números y realiza una
media aritmética:
5) Realiza un programa que pida al usuario un número entero del 0 al 9, y que mientras el número no sea correcto se repita el
proceso. Luego debe comprobar si el número se encuentra en la lista de números y notificarlo:
Consejo: La sintaxis "valor in lista" permite comprobar fácilmente si un valor se encuentra en una lista (devuelve True o False)
while True:
numero = int(input("Escribe un número del 0 al 9: "))
if numero >= 0 and numero <= 9:
break
if numero in numeros:
print("El número",numero,"se encuentra en la lista",numeros)
else:
print("El número",numero,"no se encuentra en la lista",numeros)
6) Utilizando la función range() y la conversión a listas genera las siguientes listas dinámicamente:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[-19, -17, -15, -13, -11, -9, -7, -5, -3, -1]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
7) Dadas dos listas, debes generar una tercera con todos los elementos que se repitan en ellas, pero no debe repetise ningún
elemento en la nueva lista:
lista_3 = []
print(lista_3)
clientes
print('Cliente no encontrado')
mostrar_cliente(clientes, '11111111A')
mostrar_cliente(clientes, '11111111Z')
Cliente no encontrado
print('Cliente no encontrado')
borrar_cliente(clientes, '22222222V')
Cliente no encontrado
borrar_cliente(clientes, '22222222B')
clientes
class Cliente:
def __str__(self):
return '{} {}'.format(self.nombre,self.apellidos)
class Empresa:
hector
<__main__.Cliente at 0x4b97150>
empresa.clientes
empresa.mostrar_cliente("11111111A")
empresa.borrar_cliente("22222222B")
empresa.clientes
[<__main__.Cliente at 0x4b97150>]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
La clase es como un molde para crear objetos
Definición de clase
class Galleta:
pass
Instanciación
una_galleta = Galleta()
otra_galleta = Galleta()
Función type()
Sirve para determinar la clase de un objeto.
type(una_galleta)
__main__.Galleta
type(10)
int
type(3.14)
float
type("Hola")
str
type([])
list
type({})
dict
type(True)
bool
def hola():
pass
type(hola)
function
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Atributos y métodos de clase
Atributos: Hacen referencia a las variables internas de la clase.
Métodos: Hacen referencia a las funciones internas de la clase.
class Galleta:
pass
una_galleta = Galleta()
una_galleta.sabor = "Salado"
una_galleta.color = "Marrón"
class Galleta:
chocolate = False
g = Galleta()
g.chocolate
False
g.chocolate = True
g.chocolate
True
Método init()
Se llama automáticamente al crear una instancia de clase.
class Galleta():
chocolate = False
def __init__(self):
print("Se acaba de crear una galleta.")
g = Galleta()
class Galleta():
chocolate = False
def __init__(self):
print("Se acaba de crear una galleta.")
def chocolatear(self):
self.chocolate = True
def tiene_chocolate(self):
if (self.chocolate):
print("Soy una galleta chocolateada :-D")
else:
print("Soy una galleta sin chocolate :-(")
g = Galleta()
g.tiene_chocolate()
g.chocolatear()
g.tiene_chocolate()
def chocolatear(self):
self.chocolate = True
def tiene_chocolate(self):
if (self.chocolate):
print("Soy una galleta chocolateada :-D")
else:
print("Soy una galleta sin chocolate :-(")
g = Galleta("salada","cuadrada")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-bfd19da23fef> in <module>()
----> 1 g = Galleta()
class Galleta():
chocolate = False
def chocolatear(self):
self.chocolate = True
def tiene_chocolate(self):
if (self.chocolate):
print("Soy una galleta chocolateada :-D")
else:
print("Soy una galleta sin chocolate :-(")
g = Galleta("salada","cuadrada")
p = Pelicula("El Padrino",175,1972)
p = Pelicula("El Padrino",175,1972)
String
Para devolver una cadena por defecto al convertir un objeto a una cadena con str(objeto):
<__main__.Pelicula at 0x20bb8318fd0>
str(10)
'10'
str(p)
class Pelicula:
# Constructor de clase
def __init__(self,titulo,duracion,lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
print("Se ha creado la película",self.titulo)
# Destructor de clase
def __del__(self):
print("Se está borrando la película", self.titulo)
p = Pelicula("El Padrino",175,1972)
str(p)
Length
Para devolver un número que simula la longitud del objeto len(objeto):
len(p)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-16-503cac95e140> in <module>()
----> 1 len(p)
class Pelicula:
# Constructor de clase
def __init__(self,titulo,duracion,lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
print("Se ha creado la película",self.titulo)
# Destructor de clase
def __del__(self):
print("Se está borrando la película", self.titulo)
p = Pelicula("El Padrino",175,1972)
len(p)
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
class Pelicula:
# Constructor de clase
def __init__(self, titulo, duracion, lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
print('Se ha creado la película:',self.titulo)
def __str__(self):
return '{} ({})'.format(self.titulo, self.lanzamiento)
def __init__(self,peliculas=[]):
self.peliculas = peliculas
def mostrar(self):
for p in self.peliculas:
print(p) # Print toma por defecto str(p)
p = Pelicula("El Padrino",175,1972)
c = Catalogo([p])
c.mostrar()
El Padrino (1972)
c.mostrar()
El Padrino (1972)
El Padrino: Parte 2 (1974)
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Encapsulación
Consiste en denegar el acceso a los atributos y métodos internos de la clase desde el exterior.
En Python no existe, pero se puede simular precediendo atributos y métodos con dos barras bajas __:
class Ejemplo:
__atributo_privado = "Soy un atributo inalcanzable desde fuera"
def __metodo_privado(self):
print("Soy un método inalcanzable desde fuera")
e = Ejemplo()
e.__atributo_privado
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-73328dd71c23> in <module>()
----> 1 e.__atributo_privado
e.__metodo_privado()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-164c67db4a9b> in <module>()
----> 1 e.__metodo_privado()
Cómo acceder
Internamente la clase sí puede acceder a sus atributos y métodos encapsulados, el truco consiste en crear sus equivalentes "publicos":
class Ejemplo:
__atributo_privado = "Soy un atributo inalcanzable desde fuera"
def __metodo_privado(self):
print("Soy un método inalcanzable desde fuera")
def atributo_publico(self):
return self.__atributo_privado
def metodo_publico(self):
return self.__metodo_privado()
e = Ejemplo()
e.atributo_publico()
e.metodo_publico()
adorno
<__main__.Producto at 0x5243810>
adorno.tipo
'ADORNO'
class Producto:
def __init__(self,referencia,nombre,pvp,descripcion):
self.referencia = referencia
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}""".format(self.referencia,self.nombre,self.pvp,self.descripcion)
Subclase Adorno
class Adorno(Producto):
pass
Subclase Alimento
class Alimento(Producto):
productor = ""
distribuidor = ""
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
print(al)
REFERENCIA 2035
NOMBRE Botella de Aceite de Oliva Extra
PVP 5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
Subclase Libro
class Libro(Producto):
isbn = ""
autor = ""
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}
ISBN\t\t{}
AUTOR\t\t{}""".format(self.referencia,self.nombre,self.pvp,self.descripcion,self.isbn,self.autor)
print(li)
REFERENCIA 2036
NOMBRE Cocina Mediterránea
PVP 9
DESCRIPCIÓN Recetas sanas y buenas
ISBN 0-123456-78-9
AUTOR Doña Juana
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Trabajando con clases heredadas en conjunto
class Producto:
def __init__(self,referencia,nombre,pvp,descripcion):
self.referencia = referencia
self.nombre = nombre
self.pvp = pvp
self.descripcion = descripcion
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
class Alimento(Producto):
productor = ""
distribuidor = ""
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
class Libro(Producto):
isbn = ""
autor = ""
def __str__(self):
return """\
REFERENCIA\t{}
NOMBRE\t\t{}
PVP\t\t{}
DESCRIPCIÓN\t{}
ISBN\t\t{}
AUTOR\t\t{}""".format(self.referencia,self.nombre,self.pvp,self.descripcion,self.isbn,self.autor)
Lista de productos
productos = [ad, al]
productos.append(li)
productos
[<__main__.Adorno at 0x14c58660940>,
<__main__.Alimento at 0x14c586608d0>,
<__main__.Libro at 0x14c58660978>]
REFERENCIA 2035
NOMBRE Botella de Aceite de Oliva Extra
PVP 5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
REFERENCIA 2036
NOMBRE Cocina Mediterránea
PVP 9
DESCRIPCIÓN Recetas sanas y buenas
ISBN 0-123456-78-9
AUTOR Doña Juana
Podemos acceder a los atributos si son compartidos entre todos los objetos
for p in productos:
print(p.referencia, p.nombre)
for p in productos:
print(p.autor)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-8-36e9baf5c1cc> in <module>()
1 for p in productos:
----> 2 print(p.autor)
Tendremos que tratar cada subclase de forma distinta, gracias a la función isistance():
for p in productos:
if( isinstance(p, Adorno) ):
print(p.referencia,p.nombre)
elif( isinstance(p, Alimento) ):
print(p.referencia,p.nombre,p.productor)
elif( isinstance(p, Libro) ):
print(p.referencia,p.nombre,p.isbn)
rebajar_producto(al, 10)
print(al_rebajado)
REFERENCIA 2038
NOMBRE Botella de Aceite de Oliva Extra
PVP 4.5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
print(al)
REFERENCIA 2035
NOMBRE Botella de Aceite de Oliva Extra
PVP 4.5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
Una copia de un objeto también hace referencia al objeto copiado (como un acceso directo)
Una copia de un objeto también hace referencia al objeto copiado (como un acceso directo)
copia_al = al
copia_al.referencia = 2038
print(copia_al)
REFERENCIA 2038
NOMBRE Botella de Aceite de Oliva Extra
PVP 4.5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
print(al)
REFERENCIA 2038
NOMBRE Botella de Aceite de Oliva Extra
PVP 4.5
DESCRIPCIÓN 250 ML
PRODUCTOR La Aceitera
DISTRIBUIDOR Distribuciones SA
l = [1,2,3]
l2 = l[:]
l2.append(4)
[1, 2, 3, 4]
Para crear una copia 100% nueva debemos utilizar el módulo copy:
import copy
copia_ad = copy.copy(ad)
print(copia_ad)
REFERENCIA 2034
NOMBRE Vaso adornado
PVP 15
DESCRIPCIÓN Vaso de porcelana adornado con árboles
copia_ad.pvp = 25
print(copia_ad)
REFERENCIA 2034
NOMBRE Vaso adornado
PVP 25
DESCRIPCIÓN Vaso de porcelana adornado con árboles
print(ad)
REFERENCIA 2034
NOMBRE Vaso adornado
PVP 15
DESCRIPCIÓN Vaso de porcelana adornado con árboles
Polimorfismo
Se refiere a una propiedad de la herencia por la que objetos de distintas subclases pueden responder a una misma acción.
El método rebajar_producto() es capaz de tomar objetos de distintas subclases y manipular el atributo pvp.
La acción de manipular el pvp funcionará siempre que los objetos tengan ése atributo, pero en el caso de no ser así, daría error.
La polimorfia es implícita en Python en todos los objetos, ya que todos son hijos de una superclase común llamada Object.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Herencia múltiple
Posibilidad de que una subclase herede de múltiples superclases.
En estos casos, Python dará prioridad a las clases más a la izquierda en el momento de la declaración de la subclase.
class A:
def __init__(self):
print("Soy de clase A")
def a(self):
print("Este método lo heredo de A")
class B:
def __init__(self):
print("Soy de clase B")
def b(self):
print("Este método lo heredo de B")
class C(B,A):
def c(self):
print("Este método es de C")
c = C()
Soy de clase B
c.a()
c.b()
c.c()
Este método es de C
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Métodos de las cadenas
upper(): Devuelve la cadena con todos sus caracteres a mayúscula
"Hola Mundo".upper()
'HOLA MUNDO'
"Hola Mundo".lower()
'hola mundo'
"hola mundo".capitalize()
'Hola mundo'
'Hola Mundo'
count(): Devuelve una cuenta de las veces que aparece una subcadena en la cadena
"Hola mundo".count('mundo')
"Hola mundo".find('mundoz')
-1
17
c.isdigit()
True
c2 = "ABC10034po"
c2.isalnum()
True
"Holamundo".isalpha()
True
False
False
True
False
"Hola mundo".startswith("Mola")
False
True
split(): Separa la cadena en subcadenas a partir de sus espacios y devuelve una lista
"Hola mundo mundo".split()[0]
'Hola'
"Hola,mundo,mundo,otra,palabra".split(',')
join(): Une todos los caracteres de una cadena utilizando un caracter de unión
",".join("Hola mundo")
'H,o,l,a, ,m,u,n,d,o'
" ".join("Hola")
'H o l a'
strip(): Borra todos los espacios por delante y detrás de una cadena y la devuelve
" Hola mundo ".strip()
'Hola mundo'
"-----Hola mundo---".strip('-')
'Hola mundo'
'H0la mund0'
'Hola mundo'
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Métodos de las listas
append(): Añade un ítem al final de la lista
lista = [1,2,3,4,5]
lista.append(6)
[1, 2, 3, 4, 5, 6]
lista.clear()
lista
[]
l1.extend(l2)
l1
[1, 2, 3, 4, 5, 6]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-1-3c3755903d17> in <module>()
----> 1 ["Hola", "mundo", "mundo"].index("mundoz")
l = [1,2,3]
l.insert(0,0)
print(l)
[0, 1, 2, 3]
l = [5,10,15,25]
l.insert(-1,20)
print(l)
print(l)
l.insert(20,999)
print(l)
l.pop()
50
print(l)
l.pop(0)
10
print(l)
print(l)
[20, 40]
l = [20,30,30,30,40]
l.remove(30)
print(l)
print(l)
Las cadenas no tienen el método .reverse() pero podemos simularlo haciendo unas conversiones...
"Hola mundo".reverse()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-58-eb5308f434bf> in <module>()
----> 1 "Hola mundo".reverse()
lista
['H', 'o', 'l', 'a', ' ', 'm', 'u', 'n', 'd', 'o']
lista.reverse()
lista.reverse()
lista
['o', 'd', 'n', 'u', 'm', ' ', 'a', 'l', 'o', 'H']
cadena = "".join(lista)
cadena
'odnum aloH'
sort(): Ordena automáticamente los ítems de una lista por su valor de menor a mayor
lista = [5,-10,35,0,-65,100]
lista.sort()
lista
Podemos utilizar el argumento reverse=True para indicar que la ordene del revés
lista.sort(reverse=True)
lista
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Métodos de los conjuntos
c = set()
c.add(2)
c.add(3)
{1, 2, 3}
c.discard(1)
{2, 3}
c.add(1)
c2 = c
c2.add(4)
{1, 2, 3, 4}
c2 = c.copy()
c2
{1, 2, 3, 4}
{1, 2, 3, 4}
c2.discard(4)
c2
{1, 2, 3}
{1, 2, 3, 4}
c2
set()
Comparación de conjuntos
c1 = {1,2,3}
c2 = {3,4,5}
c3 = {-1,99}
c4 = {1,2,3,4,5}
c1.isdisjoint(c2)
False
c3.issubset(c4)
False
c3.issuperset(c1)
False
Métodos avanzados
Se utilizan para realizar uniones, diferencias y otras operaciones avanzadas entre conjuntos.
Suelen tener dos formas, la normal que devuelve el resultado, y otra que hace lo mismo pero actualiza el propio resultado.
c1 = {1,2,3}
c2 = {3,4,5}
c3 = {-1,99}
c4 = {1,2,3,4,5}
True
c1.union(c2)
{1, 2, 3, 4, 5}
c1
{1, 2, 3}
c2
{3, 4, 5}
c1.update(c2)
c1
{1, 2, 3, 4, 5}
c1 = {1,2,3}
c2 = {3,4,5}
c3 = {-1,99}
c4 = {1,2,3,4,5}
c1.difference(c2)
c1.difference(c2)
{1, 2}
c1
{1, 2}
c1.intersection(c2)
{3}
{3}
c1 = {1,2,3}
c2 = {3,4,5}
c3 = {-1,99}
c4 = {1,2,3,4,5}
c1.symmetric_difference(c2)
{1, 2, 4, 5}
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Métodos de los diccionarios
colores = { "amarillo":"yellow", "azul":"blue", "verde":"green" }
colores['amarillo']
'yellow'
get(): Busca un elemento a partir de su clave y si no lo encuentra devuelve un valor por defecto
colores.get('negro','no se encuentra')
'no se encuentra'
'amarillo' in colores
True
colores.keys()
colores.values()
for c in colores:
print(colores[c])
yellow
blue
green
amarillo yellow
azul blue
verde green
pop(): Extrae un registro de un diccionario a partir de su clave y lo borra, acepta valor por
defecto
colores.pop("amarillo","no se ha encontrado")
'yellow'
colores
colores.pop("negro","no se ha encontrado")
'no se ha encontrado'
colores
{}
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
El módulo collections
Contador
Es una subclase de diccionario utilizada para realizar cuentas.
l = [1,2,3,4,1,2,3,1,2,1]
Counter(l)
Counter({1: 4, 2: 3, 3: 2, 4: 1})
p = "palabra"
Counter(p)
Counter(animales)
Counter({' ': 5,
'a': 5,
'c': 2,
'e': 3,
'g': 1,
'i': 2,
'n': 2,
'o': 6,
'p': 3,
'r': 8,
't': 1})
animales.split()
Counter(animales.split())
c = Counter(animales.split())
c.most_common(1)
[('perro', 3)]
c.most_common(2)
c.most_common()
l = [10,20,30,40,10,20,30,10,20,10]
c = Counter(l)
c.items()
c.keys()
c.values()
dict_values([1, 4, 3, 2])
sum(c.values())
10
list(c)
dict(c)
d = dict(c)
d.most_common(1)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-30-4fdd6ba29964> in <module>()
----> 1 d.most_common(1)
set(c)
d = {}
d['algo']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-38-c4e2998bb821> in <module>()
----> 1 d['algo']
KeyError: 'algo'
d = defaultdict(float)
d['algo']
0.0
d = defaultdict(str)
d['algo']
''
d = defaultdict(object)
d['algo']
<object at 0x1ad7f3201f0>
d = defaultdict(int)
d['algo'] = 10.5
d['algo']
d['algo']
10.5
d['algomas']
n = {}
n['uno'] = 'one'
n['dos'] = 'two'
n['tres'] = 'three'
Diccionarios ordenados
Otra subclase de diccionario que conserva el orden en que añadimos los registros.
n = OrderedDict()
n['uno'] = 'one'
n['dos'] = 'two'
n['tres'] = 'three'
n1 = {}
n1['uno'] = 'one'
n1['dos'] = 'two'
n2 = {}
n2['dos'] = 'two'
n2['uno'] = 'one'
n1 == n2
True
n1 = OrderedDict()
n1['uno'] = 'one'
n1['dos'] = 'two'
n2 = OrderedDict()
n2['dos'] = 'two'
n2['uno'] = 'one'
n1 == n2
False
t = (20,40,60)
t[0]
20
t[-1]
60
p = Persona(nombre="Hector",apellido="Costa",edad=27)
p.nombre
'Hector'
p.apellido
'Costa'
p.edad
27
p[0]
'Hector'
p[1]
'Costa'
p[-1]
27
p.nombre = "Hola"
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-91-5cd7aba08457> in <module>()
----> 1 p.nombre = "Hola"
El objeto datetime
dt = datetime.datetime.now() # Ahora
dt
dt.year # año
2016
dt.month # mes
dt.day # día
18
dt.hour # hora
21
dt.minute # minutos
29
dt.second # segundos
28
dt.microsecond # microsegundos
607208
21:29:28
18/6/2016
Crear un datetime manualmente (year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
Notad que sólo son obligatorios el año, el mes y el día
dt = datetime.datetime(2000,1,1)
dt
datetime.datetime(2000, 1, 1, 0, 0)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-18-f655491f2afa> in <module>()
----> 1 dt.year = 3000
dt
datetime.datetime(3000, 1, 1, 0, 0)
Formateos
Formato automático ISO (Organización Internacional de Normalización)
dt = datetime.datetime.now()
dt.isoformat()
'2016-06-18T21:37:10.303386'
dt.strftime("%A %d %B %Y %I:%M")
Códigos de idiomas
https://msdn.microsoft.com/es-es/es/library/cdax410z.aspx
import locale
'es-ES'
dt.strftime("%A %d %B %Y %I:%M")
dt
dentro_de_dos_semanas = dt + t
dentro_de_dos_semanas
hace_dos_semanas = dt - t
import pytz
pytz.all_timezones
['Africa/Abidjan',
'Africa/Accra',
'Africa/Addis_Ababa',
'Africa/Algiers',
'Africa/Asmara',
'Africa/Asmera',
'Africa/Bamako',
'Africa/Bangui',
'Africa/Banjul',
'Africa/Bissau',
'Africa/Blantyre',
'Africa/Brazzaville',
'Africa/Bujumbura',
'Africa/Cairo',
'Africa/Casablanca',
'Africa/Ceuta',
'Africa/Conakry',
'Africa/Dakar',
'Africa/Dar_es_Salaam',
'Africa/Djibouti',
'Africa/Douala',
'Africa/El_Aaiun',
'Africa/Freetown',
'Africa/Gaborone',
'Africa/Harare',
'Africa/Johannesburg',
'Africa/Juba',
'Africa/Kampala',
'Africa/Khartoum',
'Africa/Kigali',
'Africa/Kinshasa',
'Africa/Lagos',
'Africa/Libreville',
'Africa/Lome',
'Africa/Luanda',
'Africa/Lubumbashi',
'Africa/Lusaka',
'Africa/Malabo',
'Africa/Maputo',
'Africa/Maseru',
'Africa/Mbabane',
'Africa/Mogadishu',
'Africa/Monrovia',
'Africa/Nairobi',
'Africa/Ndjamena',
'Africa/Niamey',
'Africa/Nouakchott',
'Africa/Ouagadougou',
'Africa/Porto-Novo',
'Africa/Sao_Tome',
'Africa/Timbuktu',
'Africa/Tripoli',
'Africa/Tunis',
'Africa/Windhoek',
'America/Adak',
'America/Anchorage',
'America/Anguilla',
'America/Antigua',
'America/Araguaina',
'America/Argentina/Buenos_Aires',
'America/Argentina/Catamarca',
'America/Argentina/ComodRivadavia',
'America/Argentina/Cordoba',
'America/Argentina/Jujuy',
'America/Argentina/La_Rioja',
'America/Argentina/Mendoza',
'America/Argentina/Rio_Gallegos',
'America/Argentina/Salta',
'America/Argentina/San_Juan',
'America/Argentina/San_Luis',
'America/Argentina/Tucuman',
'America/Argentina/Ushuaia',
'America/Aruba',
'America/Asuncion',
'America/Atikokan',
'America/Atka',
'America/Bahia',
'America/Bahia_Banderas',
'America/Barbados',
'America/Belem',
'America/Belize',
'America/Blanc-Sablon',
'America/Boa_Vista',
'America/Bogota',
'America/Boise',
'America/Buenos_Aires',
'America/Cambridge_Bay',
'America/Campo_Grande',
'America/Cancun',
'America/Caracas',
'America/Catamarca',
'America/Cayenne',
'America/Cayman',
'America/Chicago',
'America/Chihuahua',
'America/Coral_Harbour',
'America/Cordoba',
'America/Costa_Rica',
'America/Creston',
'America/Cuiaba',
'America/Curacao',
'America/Danmarkshavn',
'America/Dawson',
'America/Dawson_Creek',
'America/Denver',
'America/Detroit',
'America/Dominica',
'America/Edmonton',
'America/Eirunepe',
'America/El_Salvador',
'America/Ensenada',
'America/Fort_Nelson',
'America/Fort_Wayne',
'America/Fortaleza',
'America/Glace_Bay',
'America/Godthab',
'America/Goose_Bay',
'America/Grand_Turk',
'America/Grenada',
'America/Guadeloupe',
'America/Guatemala',
'America/Guayaquil',
'America/Guyana',
'America/Halifax',
'America/Havana',
'America/Hermosillo',
'America/Indiana/Indianapolis',
'America/Indiana/Knox',
'America/Indiana/Marengo',
'America/Indiana/Petersburg',
'America/Indiana/Tell_City',
'America/Indiana/Vevay',
'America/Indiana/Vincennes',
'America/Indiana/Winamac',
'America/Indianapolis',
'America/Inuvik',
'America/Iqaluit',
'America/Jamaica',
'America/Jujuy',
'America/Juneau',
'America/Kentucky/Louisville',
'America/Kentucky/Monticello',
'America/Knox_IN',
'America/Kralendijk',
'America/La_Paz',
'America/Lima',
'America/Los_Angeles',
'America/Louisville',
'America/Lower_Princes',
'America/Maceio',
'America/Managua',
'America/Manaus',
'America/Marigot',
'America/Martinique',
'America/Matamoros',
'America/Mazatlan',
'America/Mendoza',
'America/Menominee',
'America/Merida',
'America/Metlakatla',
'America/Mexico_City',
'America/Miquelon',
'America/Moncton',
'America/Monterrey',
'America/Montevideo',
'America/Montreal',
'America/Montserrat',
'America/Nassau',
'America/New_York',
'America/Nipigon',
'America/Nome',
'America/Noronha',
'America/North_Dakota/Beulah',
'America/North_Dakota/Center',
'America/North_Dakota/New_Salem',
'America/Ojinaga',
'America/Panama',
'America/Pangnirtung',
'America/Paramaribo',
'America/Phoenix',
'America/Port-au-Prince',
'America/Port_of_Spain',
'America/Porto_Acre',
'America/Porto_Velho',
'America/Puerto_Rico',
'America/Rainy_River',
'America/Rankin_Inlet',
'America/Recife',
'America/Regina',
'America/Resolute',
'America/Rio_Branco',
'America/Rosario',
'America/Santa_Isabel',
'America/Santarem',
'America/Santiago',
'America/Santo_Domingo',
'America/Sao_Paulo',
'America/Scoresbysund',
'America/Shiprock',
'America/Sitka',
'America/St_Barthelemy',
'America/St_Johns',
'America/St_Kitts',
'America/St_Lucia',
'America/St_Thomas',
'America/St_Vincent',
'America/Swift_Current',
'America/Tegucigalpa',
'America/Thule',
'America/Thunder_Bay',
'America/Tijuana',
'America/Toronto',
'America/Tortola',
'America/Vancouver',
'America/Virgin',
'America/Whitehorse',
'America/Winnipeg',
'America/Yakutat',
'America/Yellowknife',
'Antarctica/Casey',
'Antarctica/Davis',
'Antarctica/DumontDUrville',
'Antarctica/Macquarie',
'Antarctica/Mawson',
'Antarctica/McMurdo',
'Antarctica/Palmer',
'Antarctica/Rothera',
'Antarctica/South_Pole',
'Antarctica/Syowa',
'Antarctica/Troll',
'Antarctica/Vostok',
'Arctic/Longyearbyen',
'Asia/Aden',
'Asia/Almaty',
'Asia/Amman',
'Asia/Anadyr',
'Asia/Aqtau',
'Asia/Aqtobe',
'Asia/Ashgabat',
'Asia/Ashkhabad',
'Asia/Baghdad',
'Asia/Bahrain',
'Asia/Baku',
'Asia/Bangkok',
'Asia/Barnaul',
'Asia/Beirut',
'Asia/Bishkek',
'Asia/Brunei',
'Asia/Calcutta',
'Asia/Chita',
'Asia/Choibalsan',
'Asia/Chongqing',
'Asia/Chungking',
'Asia/Colombo',
'Asia/Dacca',
'Asia/Damascus',
'Asia/Dhaka',
'Asia/Dili',
'Asia/Dubai',
'Asia/Dushanbe',
'Asia/Gaza',
'Asia/Harbin',
'Asia/Hebron',
'Asia/Ho_Chi_Minh',
'Asia/Hong_Kong',
'Asia/Hovd',
'Asia/Irkutsk',
'Asia/Istanbul',
'Asia/Jakarta',
'Asia/Jayapura',
'Asia/Jerusalem',
'Asia/Kabul',
'Asia/Kamchatka',
'Asia/Karachi',
'Asia/Kashgar',
'Asia/Kathmandu',
'Asia/Katmandu',
'Asia/Khandyga',
'Asia/Kolkata',
'Asia/Krasnoyarsk',
'Asia/Kuala_Lumpur',
'Asia/Kuching',
'Asia/Kuwait',
'Asia/Macao',
'Asia/Macau',
'Asia/Magadan',
'Asia/Makassar',
'Asia/Manila',
'Asia/Muscat',
'Asia/Nicosia',
'Asia/Novokuznetsk',
'Asia/Novosibirsk',
'Asia/Omsk',
'Asia/Oral',
'Asia/Phnom_Penh',
'Asia/Pontianak',
'Asia/Pyongyang',
'Asia/Qatar',
'Asia/Qyzylorda',
'Asia/Rangoon',
'Asia/Riyadh',
'Asia/Saigon',
'Asia/Sakhalin',
'Asia/Samarkand',
'Asia/Seoul',
'Asia/Shanghai',
'Asia/Singapore',
'Asia/Srednekolymsk',
'Asia/Taipei',
'Asia/Tashkent',
'Asia/Tbilisi',
'Asia/Tehran',
'Asia/Tel_Aviv',
'Asia/Thimbu',
'Asia/Thimphu',
'Asia/Tokyo',
'Asia/Tomsk',
'Asia/Ujung_Pandang',
'Asia/Ulaanbaatar',
'Asia/Ulan_Bator',
'Asia/Urumqi',
'Asia/Ust-Nera',
'Asia/Vientiane',
'Asia/Vladivostok',
'Asia/Yakutsk',
'Asia/Yekaterinburg',
'Asia/Yerevan',
'Atlantic/Azores',
'Atlantic/Bermuda',
'Atlantic/Canary',
'Atlantic/Cape_Verde',
'Atlantic/Faeroe',
'Atlantic/Faroe',
'Atlantic/Jan_Mayen',
'Atlantic/Madeira',
'Atlantic/Reykjavik',
'Atlantic/South_Georgia',
'Atlantic/St_Helena',
'Atlantic/Stanley',
'Australia/ACT',
'Australia/Adelaide',
'Australia/Brisbane',
'Australia/Broken_Hill',
'Australia/Canberra',
'Australia/Currie',
'Australia/Darwin',
'Australia/Eucla',
'Australia/Hobart',
'Australia/LHI',
'Australia/Lindeman',
'Australia/Lord_Howe',
'Australia/Melbourne',
'Australia/NSW',
'Australia/North',
'Australia/Perth',
'Australia/Queensland',
'Australia/South',
'Australia/Sydney',
'Australia/Tasmania',
'Australia/Victoria',
'Australia/West',
'Australia/Yancowinna',
'Brazil/Acre',
'Brazil/DeNoronha',
'Brazil/East',
'Brazil/West',
'CET',
'CST6CDT',
'Canada/Atlantic',
'Canada/Central',
'Canada/East-Saskatchewan',
'Canada/Eastern',
'Canada/Mountain',
'Canada/Newfoundland',
'Canada/Pacific',
'Canada/Saskatchewan',
'Canada/Yukon',
'Chile/Continental',
'Chile/EasterIsland',
'Cuba',
'EET',
'EST',
'EST5EDT',
'Egypt',
'Eire',
'Etc/GMT',
'Etc/GMT+0',
'Etc/GMT+1',
'Etc/GMT+10',
'Etc/GMT+11',
'Etc/GMT+12',
'Etc/GMT+2',
'Etc/GMT+3',
'Etc/GMT+4',
'Etc/GMT+5',
'Etc/GMT+6',
'Etc/GMT+7',
'Etc/GMT+8',
'Etc/GMT+9',
'Etc/GMT-0',
'Etc/GMT-1',
'Etc/GMT-10',
'Etc/GMT-11',
'Etc/GMT-12',
'Etc/GMT-13',
'Etc/GMT-14',
'Etc/GMT-2',
'Etc/GMT-3',
'Etc/GMT-4',
'Etc/GMT-5',
'Etc/GMT-6',
'Etc/GMT-7',
'Etc/GMT-8',
'Etc/GMT-9',
'Etc/GMT0',
'Etc/Greenwich',
'Etc/UCT',
'Etc/UTC',
'Etc/Universal',
'Etc/Zulu',
'Europe/Amsterdam',
'Europe/Andorra',
'Europe/Astrakhan',
'Europe/Athens',
'Europe/Belfast',
'Europe/Belgrade',
'Europe/Berlin',
'Europe/Bratislava',
'Europe/Brussels',
'Europe/Bucharest',
'Europe/Budapest',
'Europe/Busingen',
'Europe/Chisinau',
'Europe/Copenhagen',
'Europe/Dublin',
'Europe/Gibraltar',
'Europe/Guernsey',
'Europe/Helsinki',
'Europe/Isle_of_Man',
'Europe/Istanbul',
'Europe/Jersey',
'Europe/Kaliningrad',
'Europe/Kiev',
'Europe/Kirov',
'Europe/Lisbon',
'Europe/Ljubljana',
'Europe/London',
'Europe/Luxembourg',
'Europe/Madrid',
'Europe/Malta',
'Europe/Mariehamn',
'Europe/Minsk',
'Europe/Monaco',
'Europe/Moscow',
'Europe/Nicosia',
'Europe/Oslo',
'Europe/Paris',
'Europe/Podgorica',
'Europe/Prague',
'Europe/Riga',
'Europe/Rome',
'Europe/Samara',
'Europe/San_Marino',
'Europe/Sarajevo',
'Europe/Simferopol',
'Europe/Skopje',
'Europe/Sofia',
'Europe/Stockholm',
'Europe/Tallinn',
'Europe/Tirane',
'Europe/Tiraspol',
'Europe/Ulyanovsk',
'Europe/Uzhgorod',
'Europe/Vaduz',
'Europe/Vatican',
'Europe/Vienna',
'Europe/Vilnius',
'Europe/Volgograd',
'Europe/Warsaw',
'Europe/Zagreb',
'Europe/Zaporozhye',
'Europe/Zurich',
'GB',
'GB-Eire',
'GMT',
'GMT+0',
'GMT-0',
'GMT0',
'Greenwich',
'HST',
'Hongkong',
'Iceland',
'Indian/Antananarivo',
'Indian/Chagos',
'Indian/Christmas',
'Indian/Cocos',
'Indian/Comoro',
'Indian/Kerguelen',
'Indian/Mahe',
'Indian/Maldives',
'Indian/Mauritius',
'Indian/Mayotte',
'Indian/Reunion',
'Iran',
'Israel',
'Jamaica',
'Japan',
'Kwajalein',
'Libya',
'MET',
'MST',
'MST7MDT',
'Mexico/BajaNorte',
'Mexico/BajaSur',
'Mexico/General',
'NZ',
'NZ-CHAT',
'Navajo',
'PRC',
'PST8PDT',
'Pacific/Apia',
'Pacific/Auckland',
'Pacific/Bougainville',
'Pacific/Chatham',
'Pacific/Chuuk',
'Pacific/Easter',
'Pacific/Efate',
'Pacific/Enderbury',
'Pacific/Fakaofo',
'Pacific/Fiji',
'Pacific/Funafuti',
'Pacific/Galapagos',
'Pacific/Gambier',
'Pacific/Guadalcanal',
'Pacific/Guam',
'Pacific/Honolulu',
'Pacific/Johnston',
'Pacific/Kiritimati',
'Pacific/Kosrae',
'Pacific/Kwajalein',
'Pacific/Majuro',
'Pacific/Marquesas',
'Pacific/Midway',
'Pacific/Nauru',
'Pacific/Niue',
'Pacific/Norfolk',
'Pacific/Noumea',
'Pacific/Pago_Pago',
'Pacific/Palau',
'Pacific/Pitcairn',
'Pacific/Pohnpei',
'Pacific/Ponape',
'Pacific/Port_Moresby',
'Pacific/Rarotonga',
'Pacific/Saipan',
'Pacific/Samoa',
'Pacific/Tahiti',
'Pacific/Tarawa',
'Pacific/Tongatapu',
'Pacific/Truk',
'Pacific/Wake',
'Pacific/Wallis',
'Pacific/Yap',
'Poland',
'Portugal',
'ROC',
'ROK',
'Singapore',
'Turkey',
'UCT',
'US/Alaska',
'US/Aleutian',
'US/Arizona',
'US/Central',
'US/East-Indiana',
'US/Eastern',
'US/Hawaii',
'US/Indiana-Starke',
'US/Michigan',
'US/Mountain',
'US/Pacific',
'US/Pacific-New',
'US/Samoa',
'UTC',
'Universal',
'W-SU',
'WET',
'Zulu']
dt = datetime.datetime.now(pytz.timezone('Asia/Tokyo'))
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
El módulo math
import math
Redondeos
pi = 3.14159
math.floor(3.99)
math.ceil(3.01)
Valor absoluto
abs(-10)
10
Sumatorio
# Sumatorio integrado
n = [0.9999999, 1, 2, 3]
sum(n)
6.999999900000001
6.9999999
Truncamiento
123
Potencias y raíces
8.0
2 ** 3 # Potencia directa
3.0
Constantes
math.pi # Constante pi
3.141592653589793
math.e # Constante e
2.718281828459045
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
El módulo random
import random
0.12539542779843138
0.3332807222427663
6.272300429556777
14
68
25
c = 'Hola mundo'
random.choice(c) # letra aleatoria
'o'
l = [1,2,3,4,5]
random.choice(l) # elemento aleatorio
[3, 4, 2, 5, 1]
[3, 4]
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Tema 11: Módulos (Soluciones)
Nota: Estos ejercicios son optativos para hacer al final de la unidad y están pensados para apoyar tu aprendizaje.
El módulo se denominará operaciones y contendrá 4 funciones para realizar una suma, una resta, un producto y una division
entres dos números. Todas ellas devolverán el resultado.
En las funciones del módulo deberá de haber tratamiento e invocación manual de errores para evitar que se quede bloqueada una
funcionalidad, eso incluye:
TypeError: En caso de que se envíen valores a las funciones que no sean números. Además deberá aparecer un mensaje que
informe "Error: Tipo de dato no válido".
ZeroDivisionError: En caso de realizar una división por cero. Además deberá aparecer un mensaje que informe "Error: No es
posible dividir entre cero".
Una vez creado el módulo, crea un script calculos.py en el mismo directorio en el que deberás importar el módulo y realizar las
siguientes instrucciones. Observa si el comportamiento es el esperado:
a, b, c, d = (10, 5, 0, "Hola")
operaciones.py
def suma(a,b):
try:
r = a + b
except TypeError:
print("Error: Tipo de dato no válido")
else:
return r
def resta(a,b):
try:
r = a - b
except TypeError:
print("Error: Tipo de dato no válido")
else:
return r
def producto(a,b):
try:
r = a * b
except TypeError:
print("Error: Tipo de dato no válido")
else:
return r
def division(a,b):
try:
r = a / b
except TypeError:
print("Error: Tipo de dato no válido")
except ZeroDivisionError:
print("Error: No es posible dividir entre cero")
else:
return r
calculos.py
from operaciones import *
a, b, c, d = (10, 5, 0, "Hola")
print( "{} + {} = {}".format(a, b, suma(a, b) ) )
print( "{} - {} = {}".format(b, d, resta(b, d) ) )
print( "{} * {} = {}".format(b, b, producto(b, b) ) )
print( "{} / {} = {}".format(a, c, division(a, c) ) )
10 + 5 = 15
Error: Tipo de dato no válido
5 - Hola = None
5 * 5 = 25
Error: No es posible dividir entre cero
10 / 0 = None
2) ¿Eres capaz de desarrollar un reloj de horas, minutos y segundos utilizando el módulo datetime con la hora actual? Hazlo en
un script llamado reloj.py y ejecútalo en la terminal.
Ayudas: El módulo time integra una función llamada sleep(segundos) que puede pausar la ejecución de un programa durante un
tiempo. Así mismo el módulo os integra la función system('cls') y system('clear') para limpiar la pantalla de la terminal en sistemas
Windows y Linux/Unix respecticamente.
reloj.py
import datetime
import time
import os
while(True):
os.system('cls') # Limpiamos la pantalla
dt = datetime.datetime.now()
print( "{}:{}:{}".format( dt.hour, dt.minute, dt.second ) )
time.sleep(1) # Esperar 1 segundo
Debe incluir una función llamada leer_numero(). Esta función tomará 3 valores: ini, fin y mensaje. El objetivo es leer por pantalla
un número >= que ini y <= que fin. Además a la hora de hacer la lectura se mostrará en el input el mensaje enviado a la función.
Finalmente se devolverá el valor. Esta función tiene que devolver un número, y tiene que repetirse hasta que el usuario no lo
escriba bien (lo que incluye cualquier valor que no sea un número del ini al fin).
Una vez la tengas creada, deberás crear una nueva función llamada generador, no recibirá ningún parámetro. Dentro leerás dos
números con la función leer_numero():
El primer numero será llamado numeros, deberá ser entre 1 y 20, ambos incluidos, y se mostrará el mensaje ¿Cuantos
números quieres generar? [1-20]:
El segundo número será llamado modo y requerirá un número entre 1 y 3, ambos incluidos. El mensaje que mostrará será:
¿Cómo quieres redondear los números? [1]Al alza [2]A la baja [3]Normal: .
Una vez sepas los números a generar y cómo redondearlos, tendrás que realizar lo siguiente:
Generarás una lista de números aleatorios decimales entre 0 y 100 con tantos números como el usuario haya indicado.
A cada uno de esos 100 números deberás redondearlos en función de lo que el usuario ha especificado en el modo.
Para cada número muestra durante el redondeo, el número normal y después del redondeo.
Finalmente devolverás la lista de números redondeados.
El objetivo de este ejercicio es practicar la reutilización de código y los módulos random y math.
generador.py
import random
import math
def leer_numero(ini,fin,mensaje):
while True:
try:
valor = int( input(mensaje) )
except:
pass
else:
if valor >= ini and valor <= fin:
break
return valor
def generador():
numeros = leer_numero(1,20,"¿Cuantos números quieres generar? [1-20]: ")
modo = leer_numero(1,3,"¿Cómo quieres redondear los números? [1]Al alza [2]A la baja [3]Normal: ")
lista = []
for i in range(numeros):
numero = random.uniform(0,101)
if modo == 1:
print("{} => {}".format(numero, math.ceil(numero)) )
numero = math.ceil(numero)
elif modo == 2:
print("{} => {}".format(numero, math.floor(numero)) )
numero = math.floor(numero)
elif modo == 3:
print("{} => {}".format(numero, round(numero)) )
numero = round(numero)
lista.append(numero)
return lista
generador()
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ficheros de texto
Crear fichero y escribir texto
texto = "Una línea con texto\nOtra línea con texto"
fichero = open('fichero.txt','w') # fichero.txt ruta donde lo crearemos, w indica modo de escritura, write (puntero
40
fichero.close()
print(texto)
fichero = open('fichero.txt','r')
texto = fichero.readlines() # leer creando una lista de líneas
fichero.close()
print(texto)
['Una línea con texto\n', 'Otra línea con texto\n', 'Otra línea más abajo del todo']
fichero.close()
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-2-c2865d5b1523> in <module>()
----> 1 fichero = open('fichero_inventado.txt','r')
fichero.readline()
''
fichero.close()
Manejando el puntero
fichero = open('fichero.txt','r')
fichero.seek(0) # Puntero al principio
fichero.read(10) # Leemos 10 carácteres
'con texto\n'
fichero.seek(0)
fichero.seek( len(fichero.readline()) ) # Leemos la primera línea y situamos el puntero al principio de la segunda
20
fichero2.write(texto)
31
fichero2.close()
fichero2.write('asdfgh')
fichero2.close()
texto
fichero.close()
lista_fichero = pickle.load(fichero)
print(lista_fichero)
[1, 2, 3, 4, 5]
class Persona:
def __init__(self,nombre):
self.nombre = nombre
def __str__(self):
return self.nombre
nombres = ["Héctor","Mario","Marta"]
personas = []
for n in nombres:
p = Persona(n)
personas.append(p)
import pickle
f = open('personas.pckl','wb')
pickle.dump(personas, f)
f.close()
f = open('personas.pckl','rb')
personas = pickle.load(f)
f.close()
for p in personas:
print(p)
Héctor
Mario
Marta
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Práctica
Catálogo de películas con ficheros y pickle
from io import open
import pickle
class Pelicula:
# Constructor de clase
def __init__(self, titulo, duracion, lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
print('Se ha creado la película:',self.titulo)
def __str__(self):
return '{} ({})'.format(self.titulo, self.lanzamiento)
class Catalogo:
peliculas = []
# Constructor de clase
def __init__(self):
self.cargar()
def agregar(self,p):
self.peliculas.append(p)
self.guardar()
def mostrar(self):
if len(self.peliculas) == 0:
print("El catálogo está vacío")
return
for p in self.peliculas:
print(p)
def cargar(self):
fichero = open('catalogo.pckl', 'ab+')
fichero.seek(0)
try:
self.peliculas = pickle.load(fichero)
except:
print("El fichero está vacío")
finally:
fichero.close()
del(fichero)
print("Se han cargado {} películas".format( len(self.peliculas) ))
def guardar(self):
fichero = open('catalogo.pckl', 'wb')
pickle.dump(self.peliculas, fichero)
fichero.close()
del(fichero)
# Destructor de clase
def __del__(self):
self.guardar() # guardado automático
print("Se ha guardado el fichero")
c.mostrar()
c.mostrar()
El Padrino (1972)
El Padrino: Parte 2 (1974)
del(c)
c.mostrar()
El Padrino (1972)
El Padrino: Parte 2 (1974)
del(c)
Se ha guardado el fichero
c = Catalogo()
c.mostrar()
El Padrino (1972)
El Padrino: Parte 2 (1974)
Prueba (2005)
del(c)
Se ha guardado el fichero
c = Catalogo()
c.mostrar()
El Padrino (1972)
El Padrino: Parte 2 (1974)
Prueba (2005)
Conclusiones
Trabajamos en memoria, no en el fichero
Nosotros decidimos cuando escribir los datos:
1. Al manipular un registro
2. Al finalizar el programa
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ficheros CSV
Valores separados por comas (comma-separated values)
Documentación: https://docs.python.org/3/library/csv.html
import csv
contactos = [
("Manuel", "Desarrollador Web", "manuel@ejemplo.com"),
("Lorena", "Gestora de proyectos", "lorena@ejemplo.com"),
("Javier", "Analista de datos", "javier@ejemplo.com"),
("Marta", "Experta en Python", "marta@ejemplo.com")
]
contactos = [
("Manuel", "Desarrollador Web", "manuel@ejemplo.com"),
("Lorena", "Gestora de proyectos", "lorena@ejemplo.com"),
("Javier", "Analista de datos", "javier@ejemplo.com"),
("Marta", "Experta en Python", "marta@ejemplo.com")
]
import json
contactos = [
("Manuel", "Desarrollador Web", "manuel@ejemplo.com"),
("Lorena", "Gestora de proyectos", "lorena@ejemplo.com"),
("Javier", "Analista de datos", "javier@ejemplo.com"),
("Marta", "Experta en Python", "marta@ejemplo.com")
]
datos = []
datos
[{'nombre': 'Manuel',
'empleo': 'Desarrollador Web',
'email': 'manuel@ejemplo.com'},
{'nombre': 'Lorena',
'empleo': 'Gestora de proyectos',
'email': 'lorena@ejemplo.com'},
{'nombre': 'Javier',
'empleo': 'Analista de datos',
'email': 'javier@ejemplo.com'},
{'nombre': 'Marta',
'empleo': 'Experta en Python',
'email': 'marta@ejemplo.com'}]
datos = None
NOTA IMPORTANTE
Todos los ejercicios de ficheros deberás realizarlos en scripts creados en el mismo directorio donde vayas a crear los ficheros de datos
tal como se te indicará en los ejercicios.
1) En este ejercicio deberás crear un script llamado personas.py que lea los datos de un fichero de texto, que transforme cada
fila en un diccionario y lo añada a una lista llamada personas. Luego rocorre las personas de la lista y paracada una muestra
de forma amigable todos sus campos.
El fichero de texto se denominará personas.txt y tendrá el siguiente contenido en texto plano (créalo previamente):
1;Carlos;Pérez;05/01/1989
2;Manuel;Heredia;26/12/1973
3;Rosa;Campos;12/06/1961
4;David;García;25/07/2006
Los campos del diccionario serán por orden: id, nombre, apellido y nacimiento.
Aviso importante: Si quieres leer un fichero que no se ha escrito directamente con Python, entonces es posible que encuentres errores
de codificación al mostrar algunos caracteres. Asegúrate de indicar la codificación del fichero manualmente durante la apertura como
argumento en el open, por ejemplo con UTF-8:
open(..., encoding="utf8")
personas.py
from io import open
personas = []
for linea in lineas:
campos = linea.replace("\n", "").split(";") # borramos los saltos de línea y separamos
persona = {"id":campos[0], "nombre":campos[1], "apellido":campos[2], "nacimiento":campos[3]}
personas.append(persona)
for p in personas:
print("(id={}) {} {} => {} ".format( p['id'], p['nombre'], p['apellido'], p['nacimiento']) )
2) En este ejercicio deberás crear un script llamado contador.py que realice varias tareas sobre un fichero llamado contador.txt
que almacenará un contador de visitas (será un número):
Nuestro script trabajará sobre un fichero contador.txt. Comprobaremos si el fichero no existe o está vacío, en ese caso lo
crearemos con el número 0. Si existe simplemente leeremos el valor del contador.
Luego a partir de un argumento:
Si se envía el argumento inc, se incrementará el contador en uno y se mostrará por pantalla.
Si se envía el argumento dec, se decrementará el contador en uno y se mostrará por pantalla.
Si no se envía ningún argumento (o algo que no sea inc o dec), se mostrará el valor del contador por pantalla.
Finalmente guardará de nuevo el valor del contador de nuevo en el fichero.
Utiliza excepciones si crees que es necesario, puedes mostrar el mensaje: Error: Fichero corrupto.
contador.py
from io import open
import sys
if len(contenido) == 0:
contenido = "0"
fichero.write(contenido)
fichero.close()
print(contador)
except:
print("Error: Fichero corrupto.")
-1
3) Utilizando como base el ejercicio de los personajes que hicimos, en este ejercicio tendrás que crear un gestor de personajes
(gestor.py) para añadir y borrar la información de los siguientes personajes:
Caballero 4 2 4 2
Guerrero 2 4 2 4
Arquero 2 4 1 8
Deberás hacer uso del módulo pickle y todos los cambios que realices se irán guardando en un fichero binario
personajes.pckl, por lo que aunque cerremos el programa los datos persistirán.
Sugerencias: El ejemplo con pickle que realizamos puede servirte como base.
gestor.py
from io import open
import pickle
class Personaje:
def __str__(self):
return "{} => {} vida, {} ataque, {} defensa, {} alcance".format(self.nombre, self.vida, self.ataque, self
class Gestor:
personajes = []
# Constructor de clase
def __init__(self):
self.cargar()
def mostrar(self):
if len(self.personajes) == 0:
print("El gestor está vacío")
return
for p in self.personajes:
print(p)
def cargar(self):
fichero = open('personajes.pckl', 'ab+')
fichero.seek(0)
try:
self.personajes = pickle.load(fichero)
except:
# print("El fichero está vacío")
pass
finally:
fichero.close()
print("Se han cargado {} personajes".format( len(self.personajes) ))
def guardar(self):
fichero = open('personajes.pckl', 'wb')
pickle.dump(self.personajes, fichero)
fichero.close()
G = Gestor()
G.agregar( Personaje("Caballero",4,2,4,2) )
G.agregar( Personaje("Guerrero",2,4,2,4) )
G.agregar( Personaje("Arquero",2,4,1,8) )
G.mostrar()
#G.mostrar()
#G.borrar("Arquero")
#G.borrar("Caballera")
#G.mostrar()
Una vez creada la tabla, si intentamos volver a crearla dará error indicándonos que esta ya existe.
import sqlite3
conexion = sqlite3.connect('ejemplo.db')
# Creamos el cursor
cursor = conexion.cursor()
# Ahora crearemos una tabla de usuarios para almacenar nombres, edades y emails
cursor.execute("CREATE TABLE usuarios (nombre VARCHAR(100), edad INTEGER, email VARCHAR(100))")
conexion.close()
---------------------------------------------------------------------------
OperationalError Traceback (most recent call last)
<ipython-input-14-279f8cd3e83f> in <module>()
7
8 # Ahora crearemos una tabla de usuarios para almacenar nombres, edades y emails
----> 9 cursor.execute("CREATE TABLE usuarios (nombre text, edad int, email text)")
10
11 # Guardamos los cambios haciendo un commit
Insertando un registro
import sqlite3
conexion = sqlite3.connect('ejemplo.db')
cursor = conexion.cursor()
conexion.close()
conexion = sqlite3.connect('ejemplo.db')
cursor = conexion.cursor()
conexion.close()
('Hector', 27, 'hector@ejemplo.com')
conexion = sqlite3.connect('ejemplo.db')
cursor = conexion.cursor()
conexion.close()
conexion = sqlite3.connect('ejemplo.db')
cursor = conexion.cursor()
# Recorremos todos los registros con fetchall, y los volvamos en una lista de usuarios
usuarios = cursor.fetchall()
conexion.close()
Utilizando DB Browser
En esta práctica vamos a analizar el contenido de nuestra base de datos utilizando un programa externo.
http://sqlitebrowser.org/
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Nota: Estos ejemplos están indicados para hacerse en scripts de código Python, no en Jupyter
Claves primarias
Una clave primaria es un campo especial de una tabla que actúa como identificador único de los registros, en otras palabras, no se
puede repetir un registro con la misma clave primaria. Por ejemplo dos usuarios con el mismo DNI:
import sqlite3
conexion = sqlite3.connect('usuarios.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
import sqlite3
conexion = sqlite3.connect('usuarios.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
---------------------------------------------------------------------------
IntegrityError Traceback (most recent call last)
<ipython-input-88-1f8a69b706db> in <module>()
6
7 # Añadimos un usuario con el mismo DNI
----> 8 cursor.execute("INSERT INTO usuarios VALUES ('11111111A', 'Fernando', 31, 'fernando@ejemplo.com')")
9
10 conexion.commit()
Campos autoincrementales
No siempre contaremos con claves primarias en nuestras tablas (como el DNI), sin embargo siempre necesitaremos uno para identificar
cada registro y poder consultarlo, modificarlo o borrarlo.
Para estas situaciones lo más útil es utilizar campos autoincrementales, campos especiales que asignan automáticamente un número
(de uno en uno) al crear un nuevo registro. Es muy útil para identificar de forma única cada registro ya que nunca se repiten.
En SQLite, si indicamos que un campo numérico entero es una clave primaria, automáticamente se tomará como un campo auto
incremental. Podemos hacerlo fácilmente así:
import sqlite3
conexion = sqlite3.connect('productos.db')
cursor = conexion.cursor()
# Las cláusulas not null indican que no puede ser campos vacíos
cursor.execute('''CREATE TABLE productos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nombre VARCHAR(100) NOT NULL,
marca VARCHAR(50) NOT NULL,
precio FLOAT NOT NULL)''')
conexion.close()
import sqlite3
conexion = sqlite3.connect('productos.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
---------------------------------------------------------------------------
OperationalError Traceback (most recent call last)
<ipython-input-96-7b99f15c4bb5> in <module>()
8 ('Pantalla 19"' 'LG', 89.95),]
9
---> 10 cursor.executemany("INSERT INTO productos VALUES (?,?,?)", productos)
11
12 conexion.commit()
Para arreglarlo cambiaremos la notación durante la inserción, especificando el valor null para el auto incremento:
import sqlite3
conexion = sqlite3.connect('productos.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
import sqlite3
conexion = sqlite3.connect('productos.db')
cursor = conexion.cursor()
# Recorremos todos los registros con fetchall, y los volvamos en una lista de usuarios
productos = cursor.fetchall()
conexion.close()
Claves únicas
El problema con las claves primarias es que sólo podemos tener un campo con esta propiedad, y si da la casualidad que utilizamos un
campo autoincremental, ya no podemos asignarla a otro campo.
Para estos casos existen las claves únicas, que nos permiten añadir otros campos únicos no repetibles.
Podemos adaptar el ejemplo de los usuarios con un campo autoincremental que haga de clave primaria, y a su vez marcar el DNI como
un campo único:
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
---------------------------------------------------------------------------
IntegrityError Traceback (most recent call last)
<ipython-input-100-fdd36d467cfc> in <module>()
6
7 # Añadimos un usuario con el mismo DNI
----> 8 cursor.execute("INSERT INTO usuarios VALUES (null, '11111111A', 'Fernando', 31, 'fernando@ejemplo.com')
")
9
10 conexion.commit()
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
# Recorremos todos los registros con fetchall, y los volvamos en una lista de usuarios
usuarios = cursor.fetchall()
conexion.close()
En la próxima lección haremos uso de las consultas UPDATE y DELETE junto a la cláusula WHERE para modificar y borrar registros
gracias a los identificadores únicos, y otras propiedades.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Nota: Estos ejemplos están indicados para hacerse en scripts de código Python, no en Jupyter
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
usuario = cursor.fetchone()
print(usuario)
conexion.close()
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
usuario = cursor.fetchone()
print(usuario)
conexion.close()
UPDATE tabla
SET columna1 = valor1, columna2 = valor2...., columnaN = valorN
WHERE [condicion]
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
# Actualizamos un registro
cursor.execute("UPDATE usuarios SET nombre='Hector Costa' WHERE dni='11111111A'")
conexion.commit()
conexion.close()
Importantísimo: No olvidar la cláusula WHERE o podéis acabar actualizando todos los registros
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
# Ahora lo borramos
cursor.execute("DELETE FROM usuarios WHERE dni='55555555E'")
conexion.commit()
conexion.close()
No te olvides el WHERE
En SQL es posible realizar actualizaciones y borrados en masa, pero las dos últimas son un poco peligrosas. Sin embargo realizarlas es
tan sencillo como olvidarnos la cláusula WHERE en el UPDATE o el DELETE.
import sqlite3
conexion = sqlite3.connect('usuarios_autoincremental.db')
cursor = conexion.cursor()
conexion.commit()
conexion.close()
[]
Normalmente para encadenar expresiones las unimos utilizando el operador lógico and:
True
La condición para poder encadenar con operadores es encontrar un punto en común entre ambas expresiones:
1 < 2 < 3
True
3 > 2 > 1
True
RECORDATORIO: Al utilizar operadores encadenados, estos se basan en comprobar el resultado de cada mínima expresión y
relacionarlos con AND. No confundir con la comparación del resultado de cada expresión con el siguiente:
numero = 35
if numero >= 0 and numero <= 100:
print("El número {} se encuentra entre 0 y 100".format(numero) )
else:
print("El número {} no se encuentra entre 0 y 100".format(numero) )
numero = 35
if 0 <= numero <= 100:
print("El número {} se encuentra entre 0 y 100".format(numero) )
else:
print("El número {} no se encuentra entre 0 y 100".format(numero) )
La comprensión de listas, del inglés list comprehensions , es una funcionalidad que nos permite crear listas avanzadas en una misma
línea de código. Esto se ve mucho mejor en la práctica, así que a lo largo de esta lección vamos a trabajar distintos ejemplos.
# Método tradicional
lista = []
for letra in 'casa':
lista.append(letra)
print(lista)
Como vemos, gracias a la comprensión de listas podemos indicar directamente cada elemento que va a formar la lista, en este caso la
letra, a la vez que definimos el for:
lista = [letra for letra in 'casa'] # La lista está formada por cada letra que recorremos en el
for
Ejemplo 2) Crear una lista con las potencias de 2 de los primeros 10 números
# Método tradicional
lista = []
for numero in range(0,11):
lista.append(numero**2)
print(lista)
De este ejemplo podemos aprender que es posible modificar al vuelo los elementos que van a formar la lista.
Ejemplo 3) Crear una lista con los todos los múltiples de 2 entre 0 y 10
# Método tradicional
lista = []
for numero in range(0,11):
if numero % 2 == 0:
lista.append(numero)
print(lista)
[0, 2, 4, 6, 8, 10]
[0, 2, 4, 6, 8, 10]
En este caso podemos observar que incluso podemos marcar una condición justo al final para añadir o no el elemento en la lista.
[numero for numero in range(0,11) if numero % 2 == 0 ] # Añadir los números del 0 al 10 cuando su
módulo de 2 sea 0
Ejemplo 4) Crear una lista de pares a partir de otra lista creada con las potencias de 2 de los primeros 10
números
# Método tradicional
lista = []
for numero in range(0,11):
lista.append(numero**2)
pares = []
for numero in lista:
if numero % 2 == 0:
pares.append(numero)
print(pares)
Crear listas a partir de listas anidadas nos permite llevar la comprensión de listas al siguiente nivel y además no hay un límite.
Por ahora te animo a hacer tus propias pruebas y practicar, ya que de ahora en adelante haremos uso de la comprensión de listas en
ejemplos y otras funcionalidades avanzadas.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Ámbitos y funciones decoradoras
NOTA: Antes de realizar esta lección debes reiniciar Jupyter Notebook para vaciar la memoria.
Introducción
No cabe duda de que Python es un lenguaje flexible, y cuando trabajamos con funciones no es una excepción.
En Python, dentro de una función podemos definir otras funciones. Con la peculiaridad de que el ámbito de estas funciones se encuentre
únicamente dentro de la función padre. Vamos a trabajar los ámbitos un poco más en profundidad:
def hola():
def bienvenido():
return "Hola!"
return bienvenido
bienvenido()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-3-f083d151b813> in <module>()
----> 1 bienvenido()
Como vemos nos da un error de que no existe. En cambio si intentamos ejecutar la función hola():
hola()
<function __main__.hola.<locals>.bienvenido>
Se devuelve la función bienvenido, y podemos apreciar dentro de su definición que existe un espacio llamado locals, el cual hace
referencia al ámbito local que abarca la función.
def hola():
def bienvenido():
return "Hola!"
hola()
lista = [1,2,3]
def hola():
numero = 50
def bienvenido():
return "Hola!"
hola()
Como podemos observar, ahora además de la función tenemos una clave con el número y el valor 50. Sin embargo no encontramos la
lista, pues esta se encuentra fuera del ámbito local. De hecho se encuentra en el ámbito global, el cual podemos mostrar con la función
reservada globals():
# Antes de ejecutar este bloque reinicia el Notebook para vaciar la memoria.
lista = [1,2,3]
def hola():
numero = 50
def bienvenido():
return "Hola!"
hola()
{'__name__': '__main__', '_oh': {}, '__doc__': 'Automatically created module for IPython interactive environmen
t', 'lista': [1, 2, 3], '__builtin__': <module 'builtins' (built-in)>, 'In': ['', '# Antes de ejecutar este blo
que reinicia el Notebook para vaciar la memoria.\nlista = [1,2,3]\n\ndef hola():\n \n numero = 50\n \n
def bienvenido():\n return "Hola!"\n \n print( globals() ) # Mostramos el ámbito global\n\nhola()
'], '_ih': ['', '# Antes de ejecutar este bloque reinicia el Notebook para vaciar la memoria.\nlista = [1,2,3]\
n\ndef hola():\n \n numero = 50\n \n def bienvenido():\n return "Hola!"\n \n print( gl
obals() ) # Mostramos el ámbito global\n\nhola()'], '__loader__': None, '__builtins__': <module 'builtins' (bu
ilt-in)>, '_dh': ['C:\\CursoPython\\Fase 4 - Temas avanzados\\Tema 15 - Funcionalidades avanzadas\\Apuntes'], '
get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0
x00000243D11F5E80>>, 'hola': <function hola at 0x00000243D1B58C80>, '_sh': <module 'IPython.core.shadowns' from
'C:\\Users\\Hector\\Anaconda3\\lib\\site-packages\\IPython\\core\\shadowns.py'>, '_': '', '_ii': '', 'Out': {},
'__package__': None, '___': '', '_iii': '', '_i': '', '__spec__': None, 'exit': <IPython.core.autocall.ZMQExitA
utocall object at 0x00000243D1A92B70>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x00000243D1A92
B70>, '_i1': '# Antes de ejecutar este bloque reinicia el Notebook para vaciar la memoria.\nlista = [1,2,3]\n\n
def hola():\n \n numero = 50\n \n def bienvenido():\n return "Hola!"\n \n print( globa
ls() ) # Mostramos el ámbito global\n\nhola()', '__': ''}
Tampoco es necesario que nos paremos a analizar el contenido, pero como podemos observar, desde el ámbito global tenemos acceso
a muchas más definiciones porque engloba a su vez todas las de sus bloques padres.
Si mostramos únicamente las claves del diccionario globals(), quizá sería más entendible:
globals().keys()
dict_keys(['_i4', '__name__', '_oh', '__doc__', 'lista', '__builtin__', 'In', '_i5', '_ih', '__loader__', '__bu
iltins__', '_dh', 'get_ipython', 'hola', '_sh', '_', '_ii', 'Out', '__package__', '___', '_iii', '_i', '__spec_
_', 'exit', '_i3', 'quit', '_i1', '_i2', '__'])
Ahora si buscamos bien encontraremos la clave lista, la cual hace referencia a la variable declarada fuera de la función. Incluso
podríamos acceder a ella como si fuera un diccionario normal:
[1, 2, 3]
[1, 2, 3]
def hola():
def bienvenido():
return "Hola!"
return bienvenido
hola()
<function __main__.hola.<locals>.bienvenido>
Por muy raro que parezca, podríamos ejecutarla llamando una segunda vez al paréntesis. La primera para hola() y la segunda para
bienvenido():
hola()()
'Hola!'
Como es realmente extraño, normalmente lo que hacemos es asignar la función a una variable y ejecutarla como si fuera una nueva
función:
bienvenido = hola()
bienvenido = hola()
bienvenido()
'Hola!'
A diferencia de las colecciones y los objetos, donde las copias se utilizaban como accesos directos, las copias de las funciones son
independientes y aunque borrásemos la original, la nueva copia seguiría existiendo:
del(hola)
bienvenido()
'Hola!'
def hola():
return "Hola!"
def test(funcion):
print( funcion() )
test(hola)
Hola Mundo
Quizá en este momento no se ocurren muchas utilidades para esta funcionalidad, pero creedme que es realmente útil cuando queremos
extender funciones ya existentes sin modificarlas. De ahí que este proceso se conozca como un decorador, y de ahí pasamos
directamente a las funciones decoradoras.
Funciones decoradoras
Una función decoradora es una función que envuelve la ejecución de otra función y permite extender su comportamiento. Están
pensadas para reutilazarlas gracias a una sintaxis de ejecución mucho más simple.
def hola():
print("Hola!")
def adios():
print("Adiós!")
Y queremos queremos crear un decorador para monitorizar cuando se ejecutan las dos funciones, avisando antes y después.
Para crear una función decoradora tenemos que recibir la función a ejecutar, y envolver su ejecución con el código a extender:
def monitorizar(funcion):
def decorar():
print("\t* Se está apunto de ejecutar la función:", funcion.__name__)
funcion()
return decorar
Ahora para realizar la monitorización deberíamos llamar al monitor ejecutando la función enviada y devuelta:
monitorizar(hola)()
Sin embargo esto no es muy cómodo, y ahí es cuando aparece la sintaxis que nos permite configurar una función decoradora en una
función normal:
@monitorizar
def hola():
print("Hola!")
@monitorizar
def adios():
print("Adiós!")
Una vez configurada la función decoradora, al utilizar las funciones se ejecutarán automáticamente dentro de la función decoradora:
hola()
print()
adios()
def decorar(*args,**kwargs):
print("\t* Se está apunto de ejecutar la función:", funcion.__name__)
funcion(*args,**kwargs)
print("\t* Se ha finalizado de ejecutar la función:", funcion.__name__)
return decorar
@monitorizar_args
def hola(nombre):
print("Hola {}!".format(nombre))
@monitorizar_args
def adios(nombre):
print("Adiós {}!".format(nombre))
hola("Héctor")
print()
adios("Héctor")
Perfecto! Ahora ya sabes qué son las funciones decoradoras y cómo utilizar el símbolo @ para automatizar su ejecución. Estas
funciones se utilizan mucho cuando trabajamos con Frameworks Web como Django, así que seguro te harán servicio si tienes pensado
aprender a utilizarlo.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Funciones generadoras
Por regla general, cuando queremos crear una lista de algún tipo, lo que hacemos es crear la lista vacía, y luego con un bucle varios
elementos e ir añadiendolos a la lista si cumplen una condición:
[0, 2, 4, 6, 8, 10]
También vimos cómo era posible utilizar la función range() para generar dinámicamente la lista en la memoria, es decir, no teníamos
que crearla en el propio código, sino que se interpretaba sobre la marcha:
[0, 2, 4, 6, 8, 10]
La verdad es que range() es una especie de función generadora. Por regla general las funciones devolvuelven un valor con return, pero
la preculiaridad de los generadores es que van cediendo valores sobre la marcha, en tiempo de ejecución.
La función generadora range(0,11), empieza cediendo el 0, luego se procesa el for comprobando si es par y lo añade a la lista, en la
siguiente iteración se cede el 1, se procesa el for se comprueba si es par, en la siguiente se cede el 2, etc.
Con esto se logra ocupar el mínimo de espacio en la memoria y podemos generar listas de millones de elementos sin necesidad de
almacenarlos previamente.
def pares(n):
for numero in range(n+1):
if numero % 2 == 0:
yield numero
pares(10)
Como vemos, en lugar de utilizar el return, la función generadora utiliza el yield, que significa ceder. Tomando un número busca todos
los pares desde 0 hasta el número+1 sirviéndonos de un range().
Sin embargo, fijaros que al imprimir el resultado, éste nos devuelve un objeto de tipo generador.
De la misma forma que recorremos un range() podemos utilizar el bucle for para recorrer todos los elementos que devuelve el
generador:
0
2
4
6
8
10
[0, 2, 4, 6, 8, 10]
Sin embargo el gran potencial de los generadores no es simplemente crear listas, de hecho como ya hemos visto, el propio resultado no
es una lista en sí mismo, sino una secuencia iterable con un montón de características únicas.
Iteradores
Por tanto las funciones generadoras devuelven un objeto que suporta un protocolo de iteración. ¿Qué nos permite hacer? Pues
evidentemente controlar el proceso de generación. Teniendo en cuenta que cada vez que la función generadora cede un elemento,
queda suspendida y se retoma el control hasta que se le pide generar el siguiente valor.
Así que vamos a tomar nuestro ejemplo de pares desde otra perspectiva, como si fuera un iterador manual, así veremos exactamente a
lo que me refiero:
pares = pares(3)
pares = pares(3)
Bien, ahora tenemos un iterador de pares con todos los números pares entre el 0 y el 3. Vamos a conseguir el primer número par:
next(pares)
Como vemos la función integrada next() nos permite acceder al siguiente elemento de la secuencia. Pero no sólo eso, si volvemos a
ejecutarla...
next(pares)
Ahora devuelve el segundo! ¿No os recuerdo esto al puntero de los ficheros? Cuando leíamos una línea, el puntero pasaba a la
siguiente y así sucesivamente. Pues aquí igual.
¿Y qué pasaría si intentamos acceder al siguiente, aún sabiendo que entre el 0 y el 3 sólo tenemos los pares 0 y 2?
next(pares)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-34-68378216ba43> in <module>()
----> 1 next(pares)
StopIteration:
Pues que nos da un error porque se ha acabado la secuencia, así que tomad nota y capturad la excepción si váis a utilizarlas sin saber
exactamente cuantos elementos os devolverá el generador.
Así que la pregunta que nos queda es ¿sólo es posible iterar secuencias generadas al vuelo? Vamos a probar con una lista:
lista = [1,2,3,4,5]
next(lista)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-38-28c22b67c419> in <module>()
1 lista = [1,2,3,4,5]
----> 2 next(lista)
3
4 cadena = "Hola"
5 next(cadena)
cadena = "Hola"
next(cadena)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-39-44ca9ed1903b> in <module>()
1 cadena = "Hola"
----> 2 next(cadena)
Pues no, no podemos iterar ninguna colección como si fuera una secuencia. Sin embargo, hay una función muy interesante que nos
permite covertir las cadenas y algunas colecciones a iteradores, la función iter():
lista = [1,2,3,4,5]
lista_iterable = iter(lista)
print( next(lista_iterable) )
print( next(lista_iterable) )
print( next(lista_iterable) )
print( next(lista_iterable) )
print( next(lista_iterable) )
1
2
3
4
5
cadena = "Hola"
cadena_iterable = iter(cadena)
print( next(cadena_iterable) )
print( next(cadena_iterable) )
print( next(cadena_iterable) )
print( next(cadena_iterable) )
H
o
l
a
Muy bien, ahora ya sabemos qué son las funciones generadores, cómo utilizarlas, y también como como convertir algunos objetos a
iteradores. Os sugiero probar por vuestra cuenta más colecciones a ver si encontráis alguna más que se pueda iterar.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Funciones Lambda
Si empiezo diciendo que las funciones o expresiones lambda sirven para crear funciones anónimas, posiblemente me diréis ¿qué me
estás contando?, así que vamos a tomarlo con calma, pues estamos ante unas de las funcionalidades más potentes de Python a la vez
que más confusas para los principiantes.
Una función anónima, como su nombre indica es una función sin nombre. ¿Es posible ejecutar una función sin referenciar un nombre?
Pues sí, en Python podemos ejecutar una función sin definirla con def. De hecho son similares pero con una diferencia fundamental:
El contenido de una función lambda debe ser una única expresión en lugar de un bloque de acciones.
Y es que más allá del sentido de función que tenemos, con su nombre y sus acciones internas, una función en su sentido más trivial
significa realizar algo sobre algo. Por tanto podríamos decir que, mientras las funciones anónimas lambda sirven para realizar funciones
simples, las funciones definidas con def sirven para manejar tareas más extensas.
Si deconstruimos una función sencilla, podemos llegar a una función lambda. Por ejemplo tomad la siguiente función para doblar un
valor:
def doblar(num):
resultado = num*2
return resultado
doblar(2)
def doblar(num):
return num*2
Esta notación simple es la que una función lambda intenta replicar, fijaros, vamos a convertir la función en una función anónima:
<function __main__.<lambda>>
¡Hualá! Aquí tenemos una función anónima con una entrada que recibe num, y una salida que devuelve num * 2.
Lo único que necesitamos hacer para utilizarla es guardarla en una variable y utilizarla tal como haríamos con una función normal:
doblar(2)
True
'aloH'
Incluso podemos enviar varios valores, por ejemplo para sumar dos números:
7
Como véis podemos realizar cualquier cosa que se nos ocurra, siempre que lo podamos definir en una sola expresión.
Por ahora lo dejamos aquí, pero en las próximas lecciones veremos como utilizar la función lambda en conjunto con otras funciones
como filter() y map(), que es cuando sale a relucir su verdadero potencial.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Función filter()
Tal como su nombre indica filter significa filtrar, y es una de mis funciones favoritas, ya que a partir de una lista o iterador, y una función
condicional, es capaz de devolver una nueva colección con los elementos filtrados que cumplan la condición.
Por ejemplo, supongamos que tenemos una lista varios números y queremos filtrarla, quedándonos únicamente con los múltiples de 5...
filter(multiple, numeros)
<filter at 0x257ac84abe0>
Si ejecutamos el filtro obtenemos un objeto de tipo filtro, pero podemos transformarlo en una lista fácilmente haciendo un cast:
Por tanto cuando utilizamos la función filter() tenemos que enviar una función condicional, pero como recordaréis, no es necesario
definirla, podemos utlizar una función anónima lambda:
Así, en una sola línea hemos definido y ejecutado el filtro utilizando una función condicional anónima y una lista de numeros.
Filtrando objetos
Sin embargo, para mí, más allá de filtrar listas con valores simples, el verdadero potencial de filter() sale a relucir cuando necesitamos
filtrar varios objetos de una lista.
Por ejemplo, dada una lista con varias personas, nos gustaría filtrar únicamente las que son menores de edad:
class Persona:
def __str__(self):
return "{} de {} años".format(self.nombre, self.edad)
personas = [
Persona("Juan", 35),
Persona("Marta", 16),
Persona("Manuel", 78),
Persona("Eduardo", 12)
]
Para hacerlo nos vamos a servir de una función lambda, comprobando el campo edad para cada persona:
Marta de 16 años
Eduardo de 12 años
Sé que es un ejemplo sencillo, pero estoy seguro que os puede servir como base para realizar filtrados en muchos de vuestros
proyectos.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Función map()
Esta función trabaja de una forma muy similar a filter(), con la diferencia que en lugar de aplicar una condición a un elemento de una
lista o secuencia, aplica una función sobre todos los elementos y como resultado se devuelve un iterable de tipo map:
def doblar(numero):
return numero*2
map(doblar, numeros)
<map at 0x212eb6e0748>
list(map(doblar, numeros))
Y podemos simplificarlo con una función lambda para substituir la llamada de una función definida:
La función map() se utiliza mucho junto a expresiones lambda ya que permite ahorrarnos el esfuerzo de crear bucles for.
Además se puede utilizar sobre más de un iterable con la condición que tengan la misma longitud.
a = [1, 2, 3, 4, 5]
b = [6, 7, 8, 9, 10]
Mapeando objetos
Evidentemente, siempre que la utilicemos correctamente podemos mapear una serie de objetos sin ningún problema:
class Persona:
def __str__(self):
return "{} de {} años".format(self.nombre, self.edad)
personas = [
Persona("Juan", 35),
Persona("Marta", 16),
Persona("Manuel", 78),
Persona("Eduardo", 12)
]
Por ejemplo una función para incrementar un año de edad a todas las personas de la lista:
def incrementar(p):
p.edad += 1
return p
Juan de 36 años
Marta de 17 años
Manuel de 79 años
Eduardo de 13 años
Claro que en este caso tenemos que utilizar una función definida porque no necesitamos actuar sobre la instancia, a no ser que nos
tomemos la molestia de rehacer todo el objeto:
personas = [
Persona("Juan", 35),
Persona("Marta", 16),
Persona("Manuel", 78),
Persona("Eduardo", 12)
]
Juan de 36 años
Marta de 17 años
Manuel de 79 años
Eduardo de 13 años
Las expresiones regulares, también conocidas como 'regex' o 'regexp', son patrones de búsqueda definidos con una sintaxis formal.
Siempre que sigamos sus reglas, podremos realizar búsquedas simples y avanzadas, que utilizadas en conjunto con otras
funcionalidades, las vuelven una de las opciones más útiles e importantes de cualquier lenguaje.
Sin embargo antes de utilizarlas hay que estar seguros de lo que hacemos, de ahí aquella famosa frase de Jamie Zawinski,
programador y hacker:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Hay gente que, cuando se enfrenta a un problema, piensa "Ya sé, usaré expresiones regulares". Ahora tienen dos
problemas.
Métodos básicos
re.search: buscar un patrón en otra cadena
import re
re.search('mágica', texto)
Como vemos, al realizar la búsqueda lo que nos encontramos es un objeto de tipo Match (encontrado), en lugar un simple True o False.
re.search('hola', texto)
Por tanto, podemos utilizar la propia funcionalidad junto a un condicional sin ningún problema:
palabra = "mágica"
if encontrado:
print("Se ha encontrado la palabra:", palabra)
else:
print("No se ha encontrado la palabra:", palabra)
Sin embargo, volviendo al objeto devuelto de tipo Match, éste nos ofrece algunas opciones interesantes.
40
46
(40, 46)
En esta cadena se encuentra una palabra mágica
Como vemos, en este objeto se esconde mucha más información de la que parece a simple vista, luego seguiremos hablando de ellos.
'Hola amiga'
Aquí se nos devuelve una lista, pero podríamos aplicar la función len() para saber el número:
len(re.findall('hola', texto))
['hola', 'hello']
Antes de continuar, y para aligerar todo el proceso, vamos a crear una función capaz de ejecutar varios patrones en una lista sobre un
texto:
['hla']
['hola']
['hoola']
Con meta-carácter *
Lo utilizaremos para definir ninguna o más repeticiones de la letra a la izquierda del meta-carácter:
Con meta-carácter +
Lo utilizaremos para definir una o más repeticiones de la letra a la izquierda del meta-carácter:
Con meta-carácter ?
Lo utilizaremos para definir una o ninguna repetición de la letra a la izquierda del meta-carácter:
['hla']
['hola']
['hoola']
['hla', 'hola']
['hola', 'hoola']
['hoola', 'hooola', 'hooooola']
['hola', 'hula']
['hala', 'hila', 'hola']
['hala', 'hela', 'hila', 'hola', 'hula']
[]
['haala', 'heeela']
['hiiiila', 'hoooooola']
Exclusión en grupos [^ ]
Cuando utilizamos grupos podemos utilizar el operador de exclusión ^ para indicar una búsqueda contraria:
['hola']
['hala', 'hela', 'hila', 'hula']
Si excluimos una expresión regular con un grupo excluido, en realidad tenemos lo mismo:
Rangos [ - ]
Otra característica que hace ultra potentes los grupos, es la capacidad de definir rangos. Ejemplos de rangos:
Tened en cuenta que cualquier rango puede ser excluido para conseguir el patrón contrario.
['hola']
['h0la']
['hola', 'Hola', 'mola']
['Hola', 'M0la']
Códigos escapados \
Si cada vez que quisiéramos definir un patrón variable tuviéramos que crear rangos, al final tendríamos expresiones regulares gigantes.
Por suerte su sintaxis también acepta una serie de caracteres escapados que tienen un significo único. Algunos de los más importantes
son:
Código Significado
\d numérico
\D no numérico
\s espacio en blanco
\S no espacio en blanco
\w alfanumérico
\W no alfanumérico
El problema que encontraremos en Python a la hora de definir código escapado, es que las cadenas no tienen en cuenta el \ a no ser
que especifiquemos que son cadenas en crudo (raw), por lo que tendremos que precedir las expresiones regulares con una 'r'.
['2016']
['Este curso de Python se publicó en el año ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
['Este', 'curso', 'de', 'Python', 'se', 'publicó', 'en', 'el', 'año', '2016']
['Este', 'curso', 'de', 'Python', 'se', 'publicó', 'en', 'el', 'año', '2016']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
Por mi parte lo vamos a dejar aquí, pero el mundo de las expresiones regulares es gigantesco y daría para un curso entero. Os animo a
seguir aprendiendo leyendo documentación y buscando ejemplos.
Documentación
Hay docenas y docenas de códigos especiales, si queréis echar un vistazo a todos ellos podéis consultar la documentación oficial:
https://docs.python.org/3.5/library/re.html#regular-expression-syntax
https://developers.google.com/edu/python/regular-expressions
https://www.tutorialspoint.com/python/python_reg_expressions.htm
http://www.python-course.eu/python3_re.php
http://www.python-course.eu/python3_re_advanced.php
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Docstrings
En Python todos los objetos cuentan con una variable especial llamada doc gracias a la que podemos describir para qué sirven los y
cómo se usan los objetos. Estas variables reciben el nombre de docstrings, cadenas de documentación.
Docstrings en funciones
Python implementa un sistema muy sencillo para establecer el valor de las docstrings, únicamente tenemos que crear un comentario en
la primera línea después de la declaración.
def hola(arg):
"""Este es el docstring de la función"""
print("Hola", arg, "!")
hola("Héctor")
Hola Héctor !
Para consultar la documentación es tan sencillo como utilizar la función reservada help y pasarle el objeto:
help(hola)
hola(arg)
Este es el docstring de la función
class Clase:
""" Este es el docstring de la clase"""
def __init__(self):
"""Este es el docstring del inicializador de clase"""
def metodo(self):
"""Este es el docstring del metodo de clase"""
o = Clase()
help(o)
class Clase(builtins.object)
| Este es el docstring de la clase
|
| Methods defined here:
|
| __init__(self)
| Este es el docstring del inicializador de clase
|
| metodo(self)
| Este es el docstring del metodo de clase
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
mi_modulo.py
def despedir():
"""Este es el docstring de la función despedir"""
print("Adiós! Me estoy despidiendo desde la función despedir() del módulo prueba")
def saludar():
"""Este es el docstring de la función saludar"""
print("Hola! Te estoy saludando desde la función saludar() del módulo prueba")
help(mi_modulo)
NAME
mi_modulo - Este es el docstring del módulo
FUNCTIONS
despedir()
Este es el docstring de la función despedir
saludar()
Este es el docstring de la función saludar
FILE
c:\cursopython\fase 4 - temas avanzados\tema 16 - docomentación y pruebas\mi_modulo.py
help(mi_modulo.despedir)
despedir()
Este es el docstring de la función despedir
Como dato curioso, también podemos listar las variables y funciones del módulo con la función dir:
dir(mi_modulo)
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'despedir',
'saludar']
Como vemos muchas de ellas son especiales, seguro que muchas os suenan, os invito a comprobar sus valores:
mi_modulo.__name__
'mi_modulo'
mi_modulo.__package__
''
mi_modulo.__doc__
Docstrings en paquetes
En el caso de los paquetes el docstring debemos establecerlo en la primera línea del inicializador init:
__init__.py
""" Este es el docstring de mi_paquete """
import mi_paquete
help(mi_paquete)
Help on package mi_paquete:
NAME
mi_paquete - Este es el docstring de mi_paquete
PACKAGE CONTENTS
adios (package)
hola (package)
FILE
c:\cursopython\fase 4 - temas avanzados\tema 16 - docomentación y pruebas\mi_paquete\__init__.py
help(print)
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
help(len)
len(obj, /)
Return the number of items in a container.
help(str)
class str(object)
| str(object='') -> str
| str(bytes_or_buffer[, encoding[, errors]]) -> str
|
| Create a new string object from the given object. If encoding or
| errors is specified, then the object must expose a data buffer
| that will be decoded using the given encoding and error handler.
| Otherwise, returns the result of object.__str__() (if defined)
| or repr(object).
| encoding defaults to sys.getdefaultencoding().
| errors defaults to 'strict'.
|
| Methods defined here:
|
| __add__(self, value, /)
| Return self+value.
|
| __contains__(self, key, /)
| Return key in self.
|
| __eq__(self, value, /)
| Return self==value.
|
| __format__(...)
| S.__format__(format_spec) -> str
|
| Return a formatted version of S as described by format_spec.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getitem__(self, key, /)
| Return self[key].
|
| __getnewargs__(...)
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __iter__(self, /)
| Implement iter(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __mod__(self, value, /)
| Return self%value.
|
| __mul__(self, value, /)
| Return self*value.n
|
| __ne__(self, value, /)
| Return self!=value.
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __repr__(self, /)
| Return repr(self).
|
| __rmod__(self, value, /)
| Return value%self.
|
| __rmul__(self, value, /)
| Return self*value.
|
| __sizeof__(...)
| S.__sizeof__() -> size of S in memory, in bytes
|
| __str__(self, /)
| Return str(self).
|
| capitalize(...)
| S.capitalize() -> str
|
| Return a capitalized version of S, i.e. make the first character
| have upper case and the rest lower case.
|
| casefold(...)
| S.casefold() -> str
|
| Return a version of S suitable for caseless comparisons.
|
| center(...)
| S.center(width[, fillchar]) -> str
|
| Return S centered in a string of length width. Padding is
| done using the specified fill character (default is a space)
|
| count(...)
| S.count(sub[, start[, end]]) -> int
|
| Return the number of non-overlapping occurrences of substring sub in
| string S[start:end]. Optional arguments start and end are
| interpreted as in slice notation.
|
| encode(...)
| S.encode(encoding='utf-8', errors='strict') -> bytes
|
| Encode S using the codec registered for encoding. Default encoding
| is 'utf-8'. errors may be given to set a different error
| handling scheme. Default is 'strict' meaning that encoding errors raise
| a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
| 'xmlcharrefreplace' as well as any other name registered with
| codecs.register_error that can handle UnicodeEncodeErrors.
|
| endswith(...)
| S.endswith(suffix[, start[, end]]) -> bool
|
| Return True if S ends with the specified suffix, False otherwise.
| With optional start, test S beginning at that position.
| With optional end, stop comparing S at that position.
| suffix can also be a tuple of strings to try.
|
| expandtabs(...)
| S.expandtabs(tabsize=8) -> str
|
| Return a copy of S where all tab characters are expanded using spaces.
| If tabsize is not given, a tab size of 8 characters is assumed.
|
| find(...)
| S.find(sub[, start[, end]]) -> int
|
| Return the lowest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| format(...)
| S.format(*args, **kwargs) -> str
|
| Return a formatted version of S, using substitutions from args and kwargs.
| The substitutions are identified by braces ('{' and '}').
|
| format_map(...)
| S.format_map(mapping) -> str
|
| Return a formatted version of S, using substitutions from mapping.
| The substitutions are identified by braces ('{' and '}').
|
| index(...)
| S.index(sub[, start[, end]]) -> int
|
| Like S.find() but raise ValueError when the substring is not found.
|
| isalnum(...)
| S.isalnum() -> bool
|
| Return True if all characters in S are alphanumeric
| and there is at least one character in S, False otherwise.
|
| isalpha(...)
| S.isalpha() -> bool
|
| Return True if all characters in S are alphabetic
| and there is at least one character in S, False otherwise.
|
| isdecimal(...)
| S.isdecimal() -> bool
|
| Return True if there are only decimal characters in S,
| False otherwise.
|
| isdigit(...)
| S.isdigit() -> bool
|
| Return True if all characters in S are digits
| and there is at least one character in S, False otherwise.
|
| isidentifier(...)
| S.isidentifier() -> bool
|
| Return True if S is a valid identifier according
| to the language definition.
|
| Use keyword.iskeyword() to test for reserved identifiers
| such as "def" and "class".
|
| islower(...)
| S.islower() -> bool
|
| Return True if all cased characters in S are lowercase and there is
| at least one cased character in S, False otherwise.
|
| isnumeric(...)
| S.isnumeric() -> bool
|
| Return True if there are only numeric characters in S,
| False otherwise.
|
| isprintable(...)
| S.isprintable() -> bool
|
| Return True if all characters in S are considered
| printable in repr() or S is empty, False otherwise.
|
| isspace(...)
| S.isspace() -> bool
|
| Return True if all characters in S are whitespace
| and there is at least one character in S, False otherwise.
|
| istitle(...)
| S.istitle() -> bool
|
| Return True if S is a titlecased string and there is at least one
| character in S, i.e. upper- and titlecase characters may only
| follow uncased characters and lowercase characters only cased ones.
| Return False otherwise.
|
| isupper(...)
| S.isupper() -> bool
|
| Return True if all cased characters in S are uppercase and there is
| at least one cased character in S, False otherwise.
|
| join(...)
| S.join(iterable) -> str
|
| Return a string which is the concatenation of the strings in the
| iterable. The separator between elements is S.
|
| ljust(...)
| S.ljust(width[, fillchar]) -> str
|
| Return S left-justified in a Unicode string of length width. Padding is
| done using the specified fill character (default is a space).
|
| lower(...)
| S.lower() -> str
|
| Return a copy of the string S converted to lowercase.
|
| lstrip(...)
| S.lstrip([chars]) -> str
|
| Return a copy of the string S with leading whitespace removed.
| If chars is given and not None, remove characters in chars instead.
|
| partition(...)
| S.partition(sep) -> (head, sep, tail)
|
| Search for the separator sep in S, and return the part before it,
| the separator itself, and the part after it. If the separator is not
| found, return S and two empty strings.
|
| replace(...)
| S.replace(old, new[, count]) -> str
|
| Return a copy of S with all occurrences of substring
| old replaced by new. If the optional argument count is
| given, only the first count occurrences are replaced.
|
| rfind(...)
| S.rfind(sub[, start[, end]]) -> int
|
| Return the highest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| rindex(...)
| S.rindex(sub[, start[, end]]) -> int
|
| Like S.rfind() but raise ValueError when the substring is not found.
|
| rjust(...)
| S.rjust(width[, fillchar]) -> str
|
| Return S right-justified in a string of length width. Padding is
| done using the specified fill character (default is a space).
|
| rpartition(...)
| S.rpartition(sep) -> (head, sep, tail)
|
| Search for the separator sep in S, starting at the end of S, and return
| the part before it, the separator itself, and the part after it. If the
| separator is not found, return two empty strings and S.
|
| rsplit(...)
| S.rsplit(sep=None, maxsplit=-1) -> list of strings
|
| Return a list of the words in S, using sep as the
| delimiter string, starting at the end of the string and
| working to the front. If maxsplit is given, at most maxsplit
| splits are done. If sep is not specified, any whitespace string
| is a separator.
|
| rstrip(...)
| S.rstrip([chars]) -> str
|
| Return a copy of the string S with trailing whitespace removed.
| If chars is given and not None, remove characters in chars instead.
|
| split(...)
| S.split(sep=None, maxsplit=-1) -> list of strings
|
| Return a list of the words in S, using sep as the
| delimiter string. If maxsplit is given, at most maxsplit
| splits are done. If sep is not specified or is None, any
| whitespace string is a separator and empty strings are
| removed from the result.
|
| splitlines(...)
| S.splitlines([keepends]) -> list of strings
|
| Return a list of the lines in S, breaking at line boundaries.
| Line breaks are not included in the resulting list unless keepends
| is given and true.
|
| startswith(...)
| S.startswith(prefix[, start[, end]]) -> bool
|
| Return True if S starts with the specified prefix, False otherwise.
| With optional start, test S beginning at that position.
| With optional end, stop comparing S at that position.
| prefix can also be a tuple of strings to try.
|
| strip(...)
| S.strip([chars]) -> str
|
| Return a copy of the string S with leading and trailing
| whitespace removed.
| If chars is given and not None, remove characters in chars instead.
|
| swapcase(...)
| S.swapcase() -> str
|
| Return a copy of S with uppercase characters converted to lowercase
| and vice versa.
|
| title(...)
| S.title() -> str
|
| Return a titlecased version of S, i.e. words start with title case
| characters, all remaining cased characters have lower case.
|
| translate(...)
| S.translate(table) -> str
|
| Return a copy of the string S in which each character has been mapped
| through the given translation table. The table must implement
| lookup/indexing via __getitem__, for instance a dictionary or list,
| mapping Unicode ordinals to Unicode ordinals, strings, or None. If
| this operation raises LookupError, the character is left untouched.
| Characters mapped to None are deleted.
|
| upper(...)
| S.upper() -> str
|
| Return a copy of S converted to uppercase.
|
| zfill(...)
| S.zfill(width) -> str
|
| Pad a numeric string S with zeros on the left, to fill a field
| of the specified width. The string S is never truncated.
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| maketrans(x, y=None, z=None, /)
| Return a translation table usable for str.translate().
|
| If there is only one argument, it must be a dictionary mapping Unicode
| ordinals (integers) or characters to Unicode ordinals, strings or None.
| Character keys will be then converted to ordinals.
| If there are two arguments, they must be strings of equal length, and
| in the resulting dictionary, each character in x will be mapped to the
| character at the same position in y. If there is a third argument, it
| must be a string, whose characters will be mapped to None in the result.
import datetime
help(datetime)
NAME
datetime - Fast implementation of the datetime type.
CLASSES
builtins.object
date
datetime
time
timedelta
tzinfo
timezone
class date(builtins.object)
| date(year, month, day) --> date object
|
| Methods defined here:
|
| __add__(self, value, /)
| Return self+value.
|
| __eq__(self, value, /)
| Return self==value.
|
| __format__(...)
| Formats self with strftime.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __radd__(self, value, /)
| Return value+self.
|
| __reduce__(...)
| __reduce__() -> (cls, state)
|
| __repr__(self, /)
| Return repr(self).
|
| __rsub__(self, value, /)
| Return value-self.
|
| __str__(self, /)
| Return str(self).
|
| __sub__(self, value, /)
| Return self-value.
|
| ctime(...)
| Return ctime() style string.
|
| fromordinal(...) from builtins.type
| int -> date corresponding to a proleptic Gregorian ordinal.
|
| fromtimestamp(...) from builtins.type
| timestamp -> local date from a POSIX timestamp (like time.time()).
|
| isocalendar(...)
| Return a 3-tuple containing ISO year, week number, and weekday.
|
| isoformat(...)
| Return string in ISO 8601 format, YYYY-MM-DD.
|
| isoweekday(...)
| Return the day of the week represented by the date.
| Monday == 1 ... Sunday == 7
|
| replace(...)
| Return date with new specified fields.
|
| strftime(...)
| format -> strftime() style string.
|
| timetuple(...)
| Return time tuple, compatible with time.localtime().
|
| today(...) from builtins.type
| Current date or datetime: same as self.__class__.fromtimestamp(time.time()).
|
| toordinal(...)
| Return proleptic Gregorian ordinal. January 1 of year 1 is day 1.
|
| weekday(...)
| Return the day of the week represented by the date.
| Monday == 0 ... Sunday == 6
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| day
|
| month
|
| year
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| max = datetime.date(9999, 12, 31)
|
| min = datetime.date(1, 1, 1)
|
| resolution = datetime.timedelta(1)
class datetime(date)
| datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
|
| The year, month and day arguments are required. tzinfo may be None, or an
| instance of a tzinfo subclass. The remaining arguments may be ints.
|
| Method resolution order:
| datetime
| date
| builtins.object
|
| Methods defined here:
|
| __add__(self, value, /)
| Return self+value.
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __radd__(self, value, /)
| Return value+self.
|
| __reduce__(...)
| __reduce__() -> (cls, state)
|
| __repr__(self, /)
| Return repr(self).
|
| __rsub__(self, value, /)
| Return value-self.
|
| __str__(self, /)
| Return str(self).
|
| __sub__(self, value, /)
| Return self-value.
|
| astimezone(...)
| tz -> convert to local time in new timezone tz
|
| combine(...) from builtins.type
| date, time -> datetime with same date and time fields
|
| ctime(...)
| Return ctime() style string.
|
| date(...)
| Return date object with same year, month and day.
|
| dst(...)
| Return self.tzinfo.dst(self).
|
| fromtimestamp(...) from builtins.type
| timestamp[, tz] -> tz's local time from POSIX timestamp.
|
| isoformat(...)
| [sep] -> string in ISO 8601 format, YYYY-MM-DDTHH:MM:SS[.mmmmmm][+HH:MM].
|
| sep is used to separate the year from the time, and defaults to 'T'.
|
| now(tz=None) from builtins.type
| Returns new datetime object representing current time local to tz.
|
| tz
| Timezone object.
|
| If no tz is specified, uses local timezone.
|
| replace(...)
| Return datetime with new specified fields.
|
| strptime(...) from builtins.type
| string, format -> new datetime parsed from a string (like time.strptime()).
|
| time(...)
| Return time object with same time but with tzinfo=None.
|
| timestamp(...)
| Return POSIX timestamp as float.
|
| timetuple(...)
| Return time tuple, compatible with time.localtime().
|
| timetz(...)
| Return time object with same time and tzinfo.
|
| tzname(...)
| Return self.tzinfo.tzname(self).
|
| utcfromtimestamp(...) from builtins.type
| Construct a naive UTC datetime from a POSIX timestamp.
|
| utcnow(...) from builtins.type
| Return a new datetime representing UTC day and time.
|
| utcoffset(...)
| Return self.tzinfo.utcoffset(self).
|
| utctimetuple(...)
| Return UTC time tuple, compatible with time.localtime().
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| hour
|
| microsecond
|
| minute
|
| second
|
| tzinfo
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| max = datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
|
| min = datetime.datetime(1, 1, 1, 0, 0)
|
| resolution = datetime.timedelta(0, 0, 1)
|
| ----------------------------------------------------------------------
| Methods inherited from date:
|
| __format__(...)
| Formats self with strftime.
|
| fromordinal(...) from builtins.type
| int -> date corresponding to a proleptic Gregorian ordinal.
|
| isocalendar(...)
| Return a 3-tuple containing ISO year, week number, and weekday.
|
| isoweekday(...)
| Return the day of the week represented by the date.
| Monday == 1 ... Sunday == 7
|
| strftime(...)
| format -> strftime() style string.
|
| today(...) from builtins.type
| Current date or datetime: same as self.__class__.fromtimestamp(time.time()).
|
| toordinal(...)
| Return proleptic Gregorian ordinal. January 1 of year 1 is day 1.
|
| weekday(...)
| Return the day of the week represented by the date.
| Monday == 0 ... Sunday == 6
|
| ----------------------------------------------------------------------
| Data descriptors inherited from date:
|
| day
|
| month
|
| year
class time(builtins.object)
| time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) --> a time object
|
| All arguments are optional. tzinfo may be None, or an instance of
| a tzinfo subclass. The remaining arguments may be ints.
|
| Methods defined here:
|
| __eq__(self, value, /)
| Return self==value.
|
| __format__(...)
| Formats self with strftime.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __reduce__(...)
| __reduce__() -> (cls, state)
|
| __repr__(self, /)
| Return repr(self).
|
| __str__(self, /)
| Return str(self).
|
| dst(...)
| Return self.tzinfo.dst(self).
|
| isoformat(...)
| Return string in ISO 8601 format, HH:MM:SS[.mmmmmm][+HH:MM].
|
| replace(...)
| Return time with new specified fields.
|
| strftime(...)
| format -> strftime() style string.
|
| tzname(...)
| Return self.tzinfo.tzname(self).
|
| utcoffset(...)
| Return self.tzinfo.utcoffset(self).
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| hour
|
| microsecond
|
| minute
|
| second
|
| tzinfo
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| max = datetime.time(23, 59, 59, 999999)
|
| min = datetime.time(0, 0)
|
| resolution = datetime.timedelta(0, 0, 1)
class timedelta(builtins.object)
| Difference between two datetime values.
|
| Methods defined here:
|
| __abs__(self, /)
| abs(self)
|
| __add__(self, value, /)
| Return self+value.
|
| __bool__(self, /)
| self != 0
|
| __divmod__(self, value, /)
| Return divmod(self, value).
|
| __eq__(self, value, /)
| Return self==value.
|
| __floordiv__(self, value, /)
| Return self//value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __lt__(self, value, /)
| Return self<value.
|
| __mod__(self, value, /)
| Return self%value.
|
| __mul__(self, value, /)
| Return self*value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __neg__(self, /)
| -self
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __pos__(self, /)
| +self
|
| __radd__(self, value, /)
| Return value+self.
|
| __rdivmod__(self, value, /)
| Return divmod(value, self).
|
| __reduce__(...)
| __reduce__() -> (cls, state)
|
| __repr__(self, /)
| Return repr(self).
|
| __rfloordiv__(self, value, /)
| Return value//self.
|
| __rmod__(self, value, /)
| Return value%self.
|
| __rmul__(self, value, /)
| Return value*self.
|
| __rsub__(self, value, /)
| Return value-self.
|
| __rtruediv__(self, value, /)
| Return value/self.
|
| __str__(self, /)
| Return str(self).
|
| __sub__(self, value, /)
| Return self-value.
|
| __truediv__(self, value, /)
| Return self/value.
|
| total_seconds(...)
| Total seconds in the duration.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| days
| Number of days.
|
| microseconds
| Number of microseconds (>= 0 and less than 1 second).
|
| seconds
| Number of seconds (>= 0 and less than 1 day).
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| max = datetime.timedelta(999999999, 86399, 999999)
|
| min = datetime.timedelta(-999999999)
|
| resolution = datetime.timedelta(0, 0, 1)
class timezone(tzinfo)
| Fixed offset from UTC implementation of tzinfo.
|
| Method resolution order:
| timezone
| tzinfo
| builtins.object
|
| Methods defined here:
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getinitargs__(...)
| pickle support
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __repr__(self, /)
| Return repr(self).
|
| __str__(self, /)
| Return str(self).
|
| dst(...)
| Return None.
|
| fromutc(...)
| datetime in UTC -> datetime in local time.
|
| tzname(...)
| If name is specified when timezone is created, returns the name. Otherwise returns offset as 'UTC(
+|-)HH:MM'.
|
| utcoffset(...)
| Return fixed offset.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| max = datetime.timezone(datetime.timedelta(0, 86340))
|
| min = datetime.timezone(datetime.timedelta(-1, 60))
|
| utc = datetime.timezone.utc
|
| ----------------------------------------------------------------------
| Methods inherited from tzinfo:
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __reduce__(...)
| -> (cls, state)
class tzinfo(builtins.object)
| Abstract base class for time zone info objects.
|
| Methods defined here:
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| __reduce__(...)
| -> (cls, state)
|
| dst(...)
| datetime -> DST offset in minutes east of UTC.
|
| fromutc(...)
| datetime in UTC -> datetime in local time.
|
| tzname(...)
| datetime -> string name of time zone.
|
| utcoffset(...)
| datetime -> timedelta showing offset from UTC, negative values indicating West of UTC
DATA
MAXYEAR = 9999
MINYEAR = 1
datetime_CAPI = <capsule object "datetime.datetime_CAPI">
FILE
c:\program files\anaconda3\lib\datetime.py
Recordad, una buena documentación siempre dará respuesta a las dos preguntas básicas: ¿Para qué sirve? y ¿Cómo se utiliza?
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Pydoc
En la lección anterior aprendimos que utilizando la función help podemos mostrar información formateada por la consola. Pues en
realidad esta función hace uso del módulo pydoc para generar la documentación a partir de sus docstrings.
Desde la terminal no podemos utilizar la función help, pero sí existe la posibilidad de utilizar pydoc de forma similar.
Por ejemplo podemos consultar la documentación de scripts, módulos y clases utilizando la sintaxis:
pydoc nombre.py
También podemos utilizar la misma sintaxis para los paquetes:
pydoc nombre_paquete
Documentación en HTML
Una función interesante de Pydoc es que podemos generar la documentación de nuestro código utilizando la siguiente sintaxis:
pydoc -w nombre.py
Si tenemos un paquete podemos generar toda la documentación de forma recursiva de la siguiente forma estando en el directorio del
paquete:
pydoc -w .\
Esto generará toda la documentación del paquete, subpaquete, clases, métodos y funciones. Está bien para publicarla en Internet, pero
también podemos consultarla directamente en local lanzando un servidor temporal, desde el directorio del paquete:
pydoc -p 8000
Aunque esto mostrará la documentación general de Python en ese momento, a parte de nuestro módulo.
Existen otras alternativas más bonitas para generar documentación, como los módulos Epydoc o Sphinx, pero al ser externos a Python
no los trataremos en este curso.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Doctest
Si por un lado las docstrings nos permitían describir documentación, los doctest nos permiten combinar pruebas en la propia
documentación.
Este concepto de integrar las pruebas en la documentación nos ayuda a mantener las pruebas actualizadas, y además nos sirve como
ejemplo de utilización del código, ayudándonos a explicar su propósito.
Para utilizar doctests hay que inidicar una línea dentro de la documentación de la siguiente forma:
>>>
De esta Python entenderá que debe ejecutar el contenido dentro del comentario como si fuera código normal, y lo hará hasta que
encuentre una línea en blanco (o llegue al final de la documentación).
Definiendo pruebas
Por regla general cada prueba va ligada a una funcionalidad, pueden ser funciones, clases o sus métodos. Por ejemplo, dada una
función suma...
Para realizar una prueba dentro de la función, vamos a ejecutar un código de prueba de la propia suma:
>>> suma(5,10)
"""
return a+b
Bien, ya tenemos la prueba, pero ahora nos falta indicarle a doctest cuál es el resultado que debería devolver la prueba, y eso lo
indicaremos en la siguiente línea:
>>>
>>> suma(5,10)
15
"""
return a+b
¡Muy bien! Ahora tenemos que ejecutar la pruebas y ver si funciona o no, pero antes tenemos que adaptar el código.
Ahora justo al final indicaremos que se ejecten las pruebas doctest de las funciones del módulo escribiendo el siguiente código abajo del
todo:
import doctest
doctest.testmod() # Notar que mod significa módulo
Esto sería suficiente, pero con el objetivo de evitar que este código se ejecute al importarlo desde otro lugar, se suele indicar de la
siguiente forma:
if __name__ == "__main__":
import doctest
doctest.testmod()
Así únicamente se lanzarán las pruebas al ejecutar directamente el módulo, y ya podremos ejecutar el módulo desde la terminal:
python test.py
Como resultado veremos que no se muestra nada. Eso no significa que no se ejecute nuestra prueba, sino que esta ha funcionado
correctamente y no hay fallos.
Si queremos podemos mostrar todo el registro de ejecución pasando un argumento -v a python justo al final:
python test.py -v
Y entonces veremos el siguiente resultado:
Trying:
suma(5,10)
Expecting:
15
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.suma
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
En el que se prueba el código suma(5,10), se espera 15 y el resultado es ok; un resumen y finalmente Test passed.
>>> suma(5,10)
15
>>> suma(0,0)
1
>>> suma(-5,7)
2
"""
return a+b
python test.py
A diferencia que antes sí que nos muestra algo, indicándonos que uno de los tests a fallado:
**********************************************************************
File "test.py", line 7, in __main__.suma
Failed example:
suma(0,0)
Expected:
1
Got:
0
**********************************************************************
1 items had failures:
1 of 3 in __main__.suma
***Test Failed*** 1 failures.
La cuestión ahora sería revisar el test si es incorrecto, o adaptar la función para que devuelve el resultado esperado en el test.
Evidentemente en este caso hemos hecho un test incorrecto a propósito así que simplemente lo borraríamos.
def palindromo(palabra):
"""Comprueba si una palabra es un palíndrimo, si lo es devuelve True y si no False
Los palíndromos son palabras o frases que se leen igual de izquierda a derecha que de derecha a izquierda.
>>> palindromo("radar")
True
>>> palindromo("somos")
True
>>> palindromo("holah")
False
"""
if palabra == palabra[::-1]:
return True
else:
return False
>>> palindromo("Ana")
True
Pues que nos falla:
**********************************************************************
File "test.py", line 11, in __main__.palindromo
Failed example:
palindromo("Ana")
Expected:
True
Got:
False
**********************************************************************
1 items had failures:
1 of 3 in __main__.palindromo
***Test Failed*** 1 failures.
Claro, es que Ana empieza por mayúscula, pero hay que recordar que un palíndrimo lo es si se pronuncia igual, por tanto las
mayúsculas no debes afectar. En este caso por tanto tendríamos que readaptar nuestro código para prevenir el error:
def palindromo(palabra):
"""Comprueba si una palabra es un palíndrimo, si lo es devuelve True y si no False
Los palíndromos son palabras o frases que se leen igual de izquierda a derecha que de derecha a izquierda.
>>> palindromo("radar")
True
>>> palindromo("somos")
True
>>> palindromo("holah")
False
>>> palindromo("Ana")
True
"""
if palabra.lower() == palabra[::-1].lower():
return True
else:
return False
**********************************************************************
Failed example:
palindromo("Atar a la rata")
Expected:
True
Got:
False
**********************************************************************
1 items had failures:
1 of 4 in __main__.palindromo
***Test Failed*** 1 failures.
¡Nos falla de nuevo! El problema en este caso son los espacios, ya que estos no se leen pero si existen en la cadena, la frase no
concuerda.
def palindromo(palabra):
"""Comprueba si una palabra es un palíndrimo, si lo es devuelve True y si no False
Los palíndromos son palabras o frases que se leen igual de izquierda a derecha que de derecha a izquierda.
>>> palindromo("radar")
True
>>> palindromo("somos")
True
>>> palindromo("holah")
False
Todavía habría algunos casos como los acentos que nos darían fallos, pero ya véis por donde va la lógica. Se trata de guiarnos a partir
de los tests para crear la función correctamente.
De hecho en el mundo de la programación hay una práctica conocida como TDD, Test Driven Development o Desarrollo guiado por
pruebas que trata de escribir las pruebas primero y luego refactorizar para ir puliendo la funcionalidad.
Es una práctica algo avanzada y no la veremos en este curso, pero vale la pena comentarla.
Tests avanzados
Hasta ahora hemos hecho unos tests muy simples, pero los doctests son muy flexibles. Algunas de sus funcionalidades interesantes
son la posibilidad de ejecutar bloques de código o la captura de excepciones.
Para crear un test que incluya un bloque de código, debemos utilizar las sentencias anidadas para simular tabulaciones:
...
def doblar(lista):
"""Dobla el valor de los elementos de una lista
>>> l = [1, 2, 3, 4, 5]
>>> doblar(l)
[2, 4, 6, 8, 10]
"""
return [n*2 for n in lista]
En este caso hemos creado la lista del test manualmente, pero podríamos generarla con un bucle utilizando sentencias anidadas:
def doblar(lista):
"""Dobla el valor de los elementos de una lista
>>> l = [1, 2, 3, 4, 5]
>>> doblar(l)
[2, 4, 6, 8, 10]
>>> l = []
>>> for i in range(10):
... l.append(i)
>>> doblar(l)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
"""
return [n*2 for n in lista]
python test.py -v
Podemos observar la ejecución del test avanzado:
Trying:
l = [1, 2, 3, 4, 5]
Expecting nothing
ok
Trying:
doblar(l)
Expecting:
[2, 4, 6, 8, 10]
ok
Trying:
l = []
Expecting nothing
ok
Trying:
for i in range(10):
l.append(i)
Expecting nothing
ok
Trying:
doblar(l)
Expecting:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
ok
1 items had no tests:
__main__
1 items passed all tests:
5 tests in __main__.doblar
5 tests in 2 items.
5 passed and 0 failed.
Test passed.
Por último vamos a volver a nuestra función suma para tratar excepciones dentro de los tests.
>>> suma(5,10)
15
>>> suma(-5,7)
2
Cadenas de texto:
>>> suma('aa','bb')
'aabb'
O listas:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> suma(a,b)
[1, 2, 3, 4, 5, 6]
"""
return a+b
Ahora sabemos que no podemos sumar tipos distintos, ¿cómo podemos tenerlo en cuenta en un test?
Pues por ahora podemos suponer un resultado y comprobar el resultado cuando falle:
>>> suma(10,"hola")
"10hola"
>>> suma(5,10)
15
>>> suma(-5,7)
2
Cadenas de texto:
>>> suma('aa','bb')
'aabb'
O listas:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> suma(a,b)
[1, 2, 3, 4, 5, 6]
>>> suma(10,"hola")
"10hola"
"""
return a+b
python test.py -v
Podemos observar el fallo:
Trying:
suma(5,10)
Expecting:
15
ok
Trying:
suma(-5,7)
Expecting:
2
ok
Trying:
suma('aa','bb')
Expecting:
'aabb'
ok
Trying:
a = [1, 2, 3]
Expecting nothing
ok
Trying:
b = [4, 5, 6]
Expecting nothing
ok
Trying:
suma(a,b)
Expecting:
[1, 2, 3, 4, 5, 6]
ok
Trying:
suma(10,"hola")
Expecting:
"10hola"
**********************************************************************
File "test.py", line 26, in __main__.suma
Failed example:
suma(10,"hola")
Exception raised:
Traceback (most recent call last):
File "C:\Program Files\Anaconda3\lib\doctest.py", line 1321, in __run
compileflags, 1), test.globs)
File "<doctest __main__.suma[6]>", line 1, in <module>
suma(10,"hola")
File "test.py", line 29, in suma
return a+b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
1 items had no tests:
__main__
**********************************************************************
1 items had failures:
1 of 7 in __main__.suma
7 tests in 2 items.
6 passed and 1 failed.
***Test Failed*** 1 failures.
Concretamente debemos fijarnos en la primera línea y última de la excepción:
>>> suma(5,10)
15
>>> suma(-5,7)
2
Cadenas de texto:
>>> suma('aa','bb')
'aabb'
O listas:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> suma(a,b)
[1, 2, 3, 4, 5, 6]
>>> suma(10,"hola")
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'int' and 'str'
"""
return a+b
Y con esto acabamos esta lección en la que hemos visto como crear tests sencillos en la documentación con doctest. En la siguiente
veremos un tipo distinto de tests más avanzados, los tests unitarios o Unittest.
Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js
Unittest
El módulo unittest, a veces referido como PyUnit, forma parte de una serie de frameworks conocidos como xUnit. Estas librerías se
encuentran en la mayoría de lenguajes y son casi un estándard a la hora de programar pruebas unitarias.
A diferencia de doctest, unittest ofrece la posibilidad de crear las pruebas en el propio código implementando una clase llamada
unittest.TestCase en la que se incluirá un kit o batería de pruebas.
Cada una de las pruebas puede devolver tres respuestas en función del resultado:
Vamos a crear una prueba unitaria muy sencilla para ver su funcionamiento en un script tests.py:
import unittest
class Pruebas(unittest.TestCase):
def test(self):
pass
if __name__ == "__main__":
unittest.main()
En este sencillo ejemplo podemos observar como heredamos de la clase unittest.TestCase para crear una batería de pruebas.
Cada método dentro de esta clase será una prueba, que en nuestro ejemplo no lanza ninguna excepción ni error, porlo que significa que
los tests pasarán correctamente, y finalmente ejecutamos el método main() para ejecutar todas las baterías:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Como vemos se ha 1 realizado 1 test y el resultado a sido OK.
import unittest
class Pruebas(unittest.TestCase):
def test(self):
raise AssertionError()
if __name__ == "__main__":
unittest.main()
Entonces el test fallaría:
F
======================================================================
FAIL: test (__main__.Pruebas)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Hector\Desktop\test.py", line 5, in test
raise AssertionError()
AssertionError
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
En el supuesto caso que dentro del test diera un error no asertivo, entonces tendríamos un Error:
import unittest
class Pruebas(unittest.TestCase):
def test(self):
1/0
if __name__ == "__main__":
unittest.main()
Entonces el test fallaría:
E
======================================================================
ERROR: test (__main__.Pruebas)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Hector\Desktop\test.py", line 5, in test
1/0
ZeroDivisionError: division by zero
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
Excepciones Asertivas
Con lo que sabemos podríamos crear tests complejos sirviéndonos de condiciones y excepciones AssertionError, pero la clase
TestCase nos provee de un montón de alternativas.
Vamos a hacer un repaso de las más comunes, recordad que siempre devolverán True o False dependiendo de si pasan o no el test:
Funciones propias
import unittest
class PruebasFunciones(unittest.TestCase):
def test_doblar(self):
self.assertEqual(doblar(10), 20)
self.assertEqual(doblar('Ab'), 'AbAb')
def test_sumar(self):
self.assertEqual(sumar(-15, 15), 0)
self.assertEqual(sumar('Ab' ,'cd'), 'Abcd')
def test_es_par(self):
self.assertEqual(es_par(11), False)
self.assertEqual(es_par(68), True)
if __name__ == '__main__':
unittest.main()
Resultado:
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Métodos de cadenas
import unittest
class PruebasMetodosCadenas(unittest.TestCase):
def test_upper(self):
self.assertEqual('hola'.upper(), 'HOLA')
def test_isupper(self):
self.assertTrue('HOLA'.isupper())
self.assertFalse('Hola'.isupper())
def test_split(self):
s = 'Hola mundo'
self.assertEqual(s.split(), ['Hola', 'mundo'])
if __name__ == '__main__':
unittest.main()
Resultado:
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Preparación y limpieza
Lo último importante a comentar es que la clase TestCase incorpora dos métodos extras.
El primero es setUp() y sirve para preparar el contexto de las pruebas, por ejemplo para escribir unos valores de prueba en un fichero
conectarse a un servidor o a una base de datos.
Luego tendríamos tearDown() para hacer lo propio con la limpieza, borrar el fichero, desconectarse del servidor o borrar las entradas de
prueba de la base de datos.
Este proceso de preparar el contexto se conoce como test fixture o accesorios de prueba.
Sólo por poner un ejemplo supongamos que necesitamos contar con una lista de elementos para realizar una serie de pruebas:
import unittest
class PruebaTestFixture(unittest.TestCase):
def setUp(self):
print("Preparando el contexto")
self.numeros = [1, 2, 3, 4, 5]
def test(self):
print("Realizando una prueba")
r = [doblar(n) for n in self.numeros]
self.assertEqual(r, [2, 4, 6, 8, 10])
def tearDown(self):
print("Destruyendo el contexto")
del(self.numeros)
if __name__ == '__main__':
unittest.main()
Resultado de la prueba:
Preparando el contexto
.
Realizando una prueba
Destruyendo el contexto
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Ahora ya sabemos cómo documentar nuestro código docstrings, generar la documentación con pydoc, introducir pruebas en las
docstrings combinando doctest, y crear pruebas avanzadas con el módulo unittest.
Estamos a un paso de finalizar con el curso, sólo nos falta ver cómo distribuir nuestros módulos y programas.
Si lo recordáis, en la lección de módulos ya os enseñé como crear un distribuible con setuptools, a lo largo de esta lección vamos a
repasar y aprender varios conceptos nuevos.
Paquete básico
Antes de comenzar es importante repasar la estructura de un paquete en Python,ya que para distribuir nuestro código es indispensable
estructurarlo dentro de un paquete:
Por lo tanto vamos a empaquetar el paquete de nombre prueba, que contiene código en el fichero modulo.py.
setup.py
El fichero de configuración incluye toda la información necesaria para realizar la instalación de nuestro paquete. Algunos campos
incluyen sólo metadatos como el nombre, la versión, la descripción o el autor. Pero otros sirven para extender la instalación.
Como sería un caos que cada desarrollador pusiera los campos que quisiera, hay una serie de parámetros comunes y avanzados, pero
como son muchos lo más común es utilizar una plantilla base como la siguiente que pasa la configuración a la función setup:
setup(name="Prueba", # Nombre
version="0.1", # Versión de desarrollo
description="Paquete de prueba", # Descripción del funcionamiento
author="Hector Costa", # Nombre del autor
author_email='me@hcosta.info', # Email del autor
license="GPL", # Licencia: MIT, GPL, GPL 2.0...
url="http://ejemplo.com", # Página oficial (si la hay)
packages=['prueba'],
)
¿Hasta aquí fácil no? Son simples metadatos para definir el paquete, con la excepción de packages, en el que tenemos que indicar
todos los paquetes que formarán parte del paquete distribuido en forma de lista.
Aunque en este caso únicamente tendríamos al paquete prueba, imaginaros que tenemos docenas de subpaquetes y tubiéramos que
añadirlos uno a uno... Pues para estos casos podemos importar una función que se encargará de buscar automáticamente los
subpaquetes, se trata de find_packages y la podemos encontrar dentro de setuptools:
setup(...
packages=find_packages()
)
Dependencias
Ahora imaginaros que en vuestro paquete algún código utiliza funciones de un módulo externo o paquete que hay que instalar
manualmente. Esto se conoce como dependencias del paquete, y por suerte podemos indicar a un parámetro que descargue todos los
paquetes en la versión que nosotros indiquemos, se trata de install_requires.
Por ejemplo imaginad que dentro de nuestro paquete necesitamos utilizar el módulo Pillow para manejar imágenes. Por regla general
podemos instalarlo desde la terminal con el comando:
setup(...,
install_requires=["pillow"],
)
Y así iríamos poniendo todas las dependencias en la lista.
Lo bueno que tiene es que podemos indicar la versión exacta que queremos instalar, por ejemplo. Si mi programa utilizase la versión
1.1.0 de Pillow tendría que poner:
setup(...,
install_requires=["pillow==1.1.0"],
)
En cambio si fuera compatible con cualquier versión a partir de la 1.1.5 podría poner:
setup(...,
install_requires=["pillow>=1.1.5"],
)
Si no indicamos una versión, se instalará automáticamente la más actual.
Luego podemos abrir el fichero y añadir las dependencias automáticamente en forma de lista. Generalmente a este fichero se le llama
requirements.txt y debe estar en el mismo directorio que setup.py:
requirements.txt
pillow==1.1.0
django>=1.10.0,<=1.10.3
pygame
setup(...,
install_requires=[i.strip() for i in open("requirements.txt").readlines()],
)
Suite Test
Otra cosa interesante que podemos hacer es adjuntar una suite de tests unitarios para nuestro paquete, ya sabéis, los que aprendimos
en la unidad anterior.
Para incluirlos tendremos indicar un parámetro en el instalador llamado test_suite, al que le pasaremos el nombre del directorio que los
contiene, por lo general llamado tests:
| setup.py
| requeriments.txt
+ prueba/
| __init__.py
| modulo.py
+ tests/
| test_pillow.py
| test_django.py
| test_pygame.py
En el setup.py:
setup(...,
test_suite="tests"
)
Luego para ejecutarlos podemos utilizar el comando:
PyPI y PIP
Por último hablemos un poco más del Python Package Index.
Como ya sabéis se trata de un repositorio público con miles y miles de paquetes creados por la enorme comunidad de Python. De hecho
yo mismo creé hace años un pequeño módulo para el framework django, os dejo el enlace por si os pica la curiosidad:
https://pypi.python.org/pypi/django-easyregistration
Sea como sea, la forma de instalar cómodamente los paquetes de PyPI es con la herramienta PIP (un acrónimo recursivo de Pip Installs
Packages), utilizando el comando pip install nombre_paquete.
Además podemos listar los paquetes instalados con pip list, borrar alguno con pip uninstall nombre_paquete o incluso instalar todas
las dependencias de un fichero requisites.txt utilizando pip install requisites.txt.
Clasificadores
Por lo tanto tenemos un repositorio inmenso, así que ¿cómo podemos añadir información para categorizar nuestro paquete en PyPI?
Pues utilizando un parámetro llamado classifiers de la siguiente forma:
setup(...,
classifiers=[
"Development Status :: 3 - Alpha",
"Topic :: Utilities",
"License :: OSI Approved :: GNU General Public License (GPL)",
],
)
Hay un montón de clasificadores, desde el estado del proyecto, el tema, las licencias, etc. Una lista completa de los clasificadores
disponibles podemos encontrarla en la propia web de PyPI: https://pypi.python.org/pypi?%3Aaction=list_classifiers
Probando el paquete
Una vez tenemos toda la información configurada, podemos probar nuestro paquete fácilmente realizando una instalación en modo
desarrollo. Para ello utilizaríamos el siguiente comando:
Este modo es muy práctico, ya que nos permite utilizar nuestro módulo en cualquier lugar y hacer modificacione sin necesidad de
reinstalarlo constamente. Eso es posible porque se utiliza desde el propio directorio.
Una vez hayamos hecho las probaturas y estemos satisfechos, podemos desinstalar el paquete de desarrollo:
Pero tenemos que tener en cuenta que una vez hecho esto, el paquete se instala en una copia interna y ya no podremos modificarlo sin
antes desinstalarlo, algo que tendremos que hacer con PIP, buscando el nombre del paquete con pip list y haciendo un pip uninstall
nombre_paquete.
Distribuyendo el paquete
Ya tenemos el paquete, hemos creado el instalador, lo hemos probado y estamos preparados para distribuirlo. Hay dos formas:
Localmente: Generando un fichero comprimido que podemos compartir con nuestros conocidos.
Públicamente: En el repositorio PyPI para que todo el mundo pueda utilizarlo.
Evidentemente si distribuimos localmente no tenemos que tener mucho cuidado, y además podemos hacer pruebas. Pero si decidimos
hacerlo públicamente tendremos que intentar que el paquete tenga un mínimo de calidad.
Localmente
Distribuir el paquete localmente es muy fáci. Simplemente tenemos que utilizar el comando:
Esto generará un directorio dist/ en la carpeta del paquete. Dentro encontraremos un fichero zip o tar.gz dependiendo de nuestro
sistema operativo.
Este fichero ya podremos compartirlo con quien queramos, y para instalarlo sólo tendremos que utilizar la herramienta pip:
Luego para desinstalarlo de la misma forma pero utilizando el nombre del paquete:
Públicamente
Aunque no voy a hacer la demostración porque ahora mismo no dispongo de un paquete para publicar en el repositorio de PyPI, sí que
os voy a enseñar los pasos a seguir para hacerlo. Lo bueno de registrar un paquete en PyPI es que podemos instalarlo desde cualquier
lugar a través de internet utilizando la herramienta PIP.
Dicho ésto, si algún día creáis un paquete de calidad y queréis compartirlo con la comunidad, lo primero es registrar una cuenta en PyPI:
https://pypi.python.org/pypi?%3Aaction=register_form
Así iniciaremos una petición para registrar nuestro paquete en el repositorio. Luego tendremos que seguir los pasos e identificarnos
cuando lo pida con nuestro usuario y contraseña (que hemos creado antes).
Una vez hecho esto ya hemos creado nuestro paquete, pero todavía no hemos publicado una versión, así que vamos a hacerlo
utilizando el comando:
¡Y ya está! Ahora podremos instalar nuestro paquete desde en cualquier lugar con PIP:
Por suerte hay un módulo que nos ayudará mucho a generar ejecutables porque automatiza el proceso, ese es pyinstaller.
Lo que hace es generar un .EXE en Windows, un .DMG en MAC o el ejecutable que utilice el sistema operativo. Dentro del ejecutable se
incluye el propio intérprete de Python, y por esa razón podremos utilizarlo en cualquier ordenador sin necesidad de instalar Python
previamente.
Instalación
La instalación es muy fácil:
No hay más.
Primer ejecutable
Comencemos con algo simple, tenemos un script hola.py:
print("Hola mundo!")
Y queremos crear un ejecutable a partir de él, pues haríamos lo siguiente:
pyinstaller hola.py
Una vez acabe el proceso se nos habrán creado varias carpetas. La que nos interesa es dist, y dentro encontraremos una carpeta con
el nombre programa y en esta un montón de ficheros y el ejecutable, en mi caso como estoy en Windows es hola.exe.
Como es un programa para terminal, para ejecutarlo tengo que abrir la terminal en ese directorio y ejecutar el programa manualmente:
C:\Users\Hector\Desktop\hola\dist\hola>hola.exe
Hola mundo!
pyinstaller hola.py
En esta ocasión si ejecutamos el programa con doble clic nos funcionará bien, el problema es que se muestra la terminal de fondo.
Para que desaparezca tenemos que indicar que es una aplicación en ventana, y eso lo hacemos de la siguiente forma al crear el
ejecutable:
Ejecutable en un fichero
Ya véis que por defecto Pyinstaller crea un directorio con un montón de ficheros. Podemos utilizar un comando para generar un solo
fichero ejecutable que lo contenga todo, pero este ocupara bastante más:
Cambiar el icono
También podemos cambiar el icono por defecto del ejecutable. Para ello necesitamos una imagen en formato .ico.
Si por algo no os cambia el icono, probad cambiando el nombre del ejecutable. A veces el caché de Windows puede ignorar estas
cosas.
Limitaciones
El gran problema con Pyinstaller como os decía al principio son las dependencias.
Si nuestro programa utiliza únicamente módulos de la librería estándard no tendremos ningún problema, pero si queremos utilizar
módulos externos es posible que no funcione... A no ser que sea alguno de los soportados como PyQT, django, pandas, matpotlib...
pero requieren una configuraciones extra.
Si queréis saber más os dejo este enlace con los paquetes soportados: https://github.com/pyinstaller/pyinstaller/wiki/Supported-
Packages
Personalmente nunca he hecho nada avanzado, pero si queréis hacerlo y no lo conseguís, os podéis poner en contacto conmigo y
estaré encantado de ayudaros.