Python 🐍

Para no programadores

David García Garzón

Sesión 1
2020-02-06
🐍

Introducción

Objetivo

Introducir el lenguaje de programación Python a personas del ET que no hayan programado nunca

Dos sesiones estilo manos a la obra (hands-on)

Meta: Leer una hoja de cálculo, procesarla y generar un informe con cara y ojos

Los ejemplos de la presentación los podeis copiar en vuestros ordenadores de la versión on-line

Código Máquina

Programamos para decirle al ordenador qué hacer

El ordenador solo entiende código máquina:
01000111001… binario ininteligible para humanos

…para casi todos

Código fuente

(El resto) usamos lenguajes intermedios
para comunicarnos con la máquina

Los lenguajes de programación nos abstraen de la máquina para concentrarnos en el problema,

y, así, llegar más lejos:

¿Cómo se pasa de Código fuente a Código máquina?

Compiladores e intérpretes

Compilador

Traductor de libros

Genera un ejecutable

Se ejecuta el binario, sin código ni compilador

C, C++, Java, Pascal…

Intérprete

Traducción simultanea

Ejecuta mientras interpreta

Necesitas interprete y código para ejecutar

Python, Perl, Bash, PHP…

Python mola

Es muy fácil de aprender

No te limita cuando quieres crecer

No (solo) para jugar: ciencia, negocios…

Hay librerías para todo

Mucha comunidad

Se llama así por los Monty Python
Tiene huevos de pascua…

Elementos

Lenguaje: Tutorial oficial

Librería estandard: Referencia

Librerías no estándards: Repositorio

Tened los enlaces a mano para consulta

Disclaimer: todo en inglés

Instalación

Linux

$ sudo apt install ipython3

Windows

Anaconda

Ejecución interactiva

Intérprete interactivo: ipython3

Ideal para pruebas rápidas

>>> print("hola mundo")
hola mundo

Completa o explora con el tabulador

Recupera la historia con el cursor arriba

Scripts

Cuando el código se complica,
mejor meterlo en un fichero (script)

# miscript.py
print("hola mundo")

Se ejecuta:

$ python3 miscript.py
hola mundo

Sintaxis general

# Despues de la almohadilla son comentarios, los ignora
print("Hola Mundo") # Una sentencia por línia
print("Hola caracola")

# Sentencias con ':' empiezan subsentencias indentadas
if True:
    print("Primera subsentencia")
    print("Segunda subsentencia")
print("Esta ya esta fuera")

if True:
    if False:
        print("Dentro del if False")
    print("Dentro del if True")

Gamberreando

# Los signos que abren y cierran permiten partir líneas
print(
    "Hola mundo"
)

# A muy malas con la contrabarra al final de línea
print \
    ("Hola mundo")

# Punto y coma para juntar dos sentencias
print("Marco"); print("Polo")

# Tambien podemos poner en una ĺínia las subsentencias
if True: print("subsentencia rápida")

Calculadora

Expresiones

Las sentencias más simples, se evalúan

Usemos ipython3 como calculadora

>>> 4+5
9

4, 5 y 9 son literales

Los literales representan datos directos

El + es un operador

Operadores numéricos

>>> 10+3 # suma
13
>>> 10-3 # resta
7
>>> 10*3 # multiplicación
30
>>> 10/3 # división con decimales
3.3333333333333335
>>> 10//3 # división entera
3
>>> 10%3 # resto de la división entera
1
>>> 10**3 # potencia
1000

Prioridad Operadores

Como en mates:

Exponenciación
Multiplicaciones y divisiones
Sumas y restas
Paréntesis para romperlo

2 * 3 + 4 # se resuelve como (2*3)+4 = 10
2 + 3 * 4 # se resuelve como 2+(3*4) = 14
2 * (3 + 4) # = 2 * 7 = 14
(2 + 3) * 4 # = 5 * 4 = 20
2 + 3 + 4 + 5 # misma prioridad, orden de lectura ((2+3)+4)+5 = 14

Asignación

Variable: nombre que referencia un valor

Mantiene valores para usarlos después

>>> a = 23  # sentencia de asignación, a un literal
>>> a       # usamos la variable como expresión
23
>>> b = 10*a   # la usamos dentro de otra expresión y asignamos
>>> b
230
>>> c = 'hola' # pueden ser cualquier tipo de valor
>>> c
'hola'

Nombres de variables

Porfa, no uses nombres tontos a, b

Usa nombres que expliquen la variable
como consumoEnKwh.

Lower Case: todojuntosinseparar
Camel Case: mayusculasAlInicioDePalabra
Underscore: separadas_con_subrayados

Ops de Actualización

Sirven para actualizar el valor de una variable operando el valor original.

saldo = 0
# Hacemos una venta
saldo = saldo + 200
# Podríamos haber escrito:
saldo += 200
costeAntena = 100
costeSwitch = 45
descuento = 20
factorIva = 0.21
costeFactura = 0
costeFactura += costeAntena
costeFactura += costeSwitch
costeFactura -= descuento
costeFactura *= 1 + factorIva
print(costeFactura)

Ejercicio: Simular Factura

Script para calcular la factura de una 2.0A

Arriba los valores de entrada en variables

Pon a las variables nombres decentes
consumoEnKwh, precioPorKwDia

Ves calculando poco a poco valores intermedios

Haz un print del importe final.

Simulador de factura

# Depende de la factura
potenciaKw = 3.3
consumoKwh = 100
diasFacturados = 30
# Fijo hasta cambio tarifas
precioKwh = 0.139
precioKwDia = 0.104229
diasAnuales = 365
alquilerContadorAnual = 9.72
bonoSocialDia = 0.02
impuestoElectricoPercent = 5.11
ivaPercent = 21

Llamando funciones

>>> max(30,500)  # le pasamos dos parámetros, retorna un valor
500
>>> min(30,500) + 4  # usamos el valor retornado en una expresión
34
>>> min(max(30,400),100)  # el resultado de una como parametro
30
>>> round(30.3262342, 2) # redondea a 2 decimales
30.33

round, max y min son funciones built-in
Podemos importar otras funciones de librerias
o definir las nuestras

Primera función propia


def aplicaPercent(valor, percent):
    return valor*(1 + percent/100)

...
totalElectricidad = aplicaPercent(
    importePotencia + importeEnergia,
    percentImpuestoElectrico)
...
importeFactura = aplicaPercent(baseImponible, percentIva)

Tipos de datos

Literales básicos

>>> -12 # un número entero (int)
-12
>>> 12.34 # un número con decimales (float)
12.34
>>> 2+3j # un número complejo (complex)
(2+3j)
>>> 'un texto' # un texto (str)
'un texto'
>>> None # el no-objeto (NoneType), el intérprete ni lo imprime
>>> True # un valor lógico (bool), su antitesis es False
True

Literales estructuras

>>> 12, 23 # una tupla (tuple) con dos enteros
(12, 23)

>>> [1, 2, 3, 1] # una lista (list) con 4 enteros
[1, 2, 3, 1]

>>> {1, 2, 3, 1} # un conjunto (set) con 4... ops, 3 enteros
{1, 2, 3}

>>> # un diccionario (dict) 2 con parejas clave: valor
>>> { 'David': 40, 'Aitor': 25 }
{ 'David': 40, 'Aitor': 25 }

Anidando

course = {
    "name": "Python for non-programers",
    "sessions": [
        {
            "name": "Session 01: Basics",
            "teacher": "David",
            "durationMinutes": 120,
            "content": [
                "Programming: the Python interpreter",
                "Calculator, managing numbers",
                "Data types",
                "Managing strings",
            ]
        },
        {
            "name": "Session 01: Tooling",
            ...
        }
    ],
}

Texto

Literales de texto str

# Varios delimitadores, útil si contiene comillas
print('Comillas simples')
print("Comillas dobles")
print'''Triples comillas simples''') # Para texto multilínia
print("""Triples comillas dobles""")

# Útiles si el texto tiene comillas
print('Me dijo: "Adios" y se fue')
print("Castellar de N'Hug")

# Tambien podemos escapar con la contrabarra
print('Usando la contrabarra para \'escapar\' la comilla')

Escapando

# Carácteres especiales indicados con contrabarra y una letra
print("Primera linia\nSegunda linia") # Salto de linia \n
print("Primer campo\tSegundo campo") # Tabulador \t
print("La contra barra: \\") # Contrabarra se escapa a si misma

Ojo, indicando ficheros en Windows:

>>> # Las contrabarras se convierten en un tab y un salto de línea
>>> print("c:\temp\newitem")
c:      emp
ewitem
>>> print("c:\\temp\\newitem") # Escapando contrabarras
c:\temp\newitem

Ops con texto

>>> "Hola" + "mundo" # juntamos los dos textos (sin espacio!)
'Holamundo'
>>> 'hola'*4 # Multiplicar por un numero, repite el texto
'holaholaholahola'
>>> len('hola') # longitud de un texto
4
>>> 'ol' in 'hola'
True
>>> 'lo' in 'hola'
False
>>> 'lo' not in 'hola'
True
>>> 'ol' not in 'hola'
False

Indexado

>>> a = 'murcielago'
>>> a[1] # ¡ojo! ¡La primera letra es la 0!
'u'
>>> a[0] # Ahora sí
'm'
>>> a[10] # ¡ojo! el ultimo es el 9, si nos salimos, error
IndexError: string index out of range
>>> a[9] # así sí
'o'
>>> a[-1] # Con índices negativos empezamos por el final
'o'
>>> a[-2]
'g'

Rebanado (slicing)

>>> a[2:6] # de la tercera (2) a la sexta letra (5)
'rcie'
>>> a[:6] # Si no ponemos el inicio se deduce desde el principio
'murcie'
>>> a[2:] # si no pones el final se deduce que hasta el fin
'rcielago'
>>> a[:] # copia entera
'murcielago'
>>> a[:5] + a[5:] # ¿porque el índice 5 no esta repetido?
'murcielago'

El índice final no se incluye

Zancadas (striding)

>>> a[::2] # saltamos las letras de dos en dos
'mrilg'
>>> a[1::2] # si le damos un offset
'uceao'
>>> a[6:2] # inicio y fin no ordenados, retorna texto vacío
''
>>> a[6:2:-1] # pero con un paso negativo, va hacia atrás
'leic'
>>> a[::-1] # Super útil, voltea la secuencia
'ogaleicrum'

Métodos de textos

>>> s = 'abracadabra'
>>> s.count('bra') # Cuantas veces contiene el parámetro
2
>>> 'abrete sesamo'.capitalize() # primera letra mayúscula
'Abrete sesamo'
>>> 'abrete sesamo'.title() # iniciales en mayúsculas
'Abrete Sesamo'
>>> s.upper() # todo a mayusculas
'ABRACADABRA'
>>> 'AbRaCaDaBra'.lower() # todo a minúsculas
'abracadabra'
>>> s.replace('a', 'o')
'obrocodobro'
>>> s.replace('ab', 'XXX')
'XXXracadXXXra'

Listas y textos

Textos y listas, ambos son secuencias de cosas.
Comparten algunas operaciones.

Ejercicio: Prueba en el interactivo:

milista = [1,2,3,4,"cinco",6,7,"ocho"]
len(milista)
milista.count('cinco')
7 in milista
milista[0]
milista[-1]
milista[3:8]
milista[::-1]

Partiendo y juntando

>>> 'abracadabra'.split('a')
['', 'br', 'c', 'd', 'br', '']

>>> 'hola tu que tal'.split()
['hola', 'tu', 'que', 'tal']

>>> '-'.join(['hola', 'tu', 'que', 'tal'])
'hola-tu-que-tal'

Ejercicio: Convertir "una frase con palabras" a un nombre de variable con underlines "una_frase_con_palabras" o en camell case "unaFraseConPalabras"

Formateando

Texto con prefijo f: entre corchetes, expresión a substituir

>>> print(f"El importe de la factura es {importeFactura}€")
El importe de la factura es 48.447344323

Indicando el formato. Mini lenguaje de formato

>>> print(f"El importe de la factura es {importeFactura:10.2f}€")
El importe de la factura es     48.45

Sesiones en vivo

¿Qué es esto?

Las siguientes diapos contienen consolas animadas con audio que reproducen la sesión en vivo que se hizo en la formación.

No es video es texto animado.
Puedes seleccionar el texto de los comandos y copiarlos en tu intérprete.

Modos de ejecución

Sintaxis general

Calculadora

Textos

PENDIENTE

Sesión 2
2020-02-13
🐍 🐍

Repaso

Modos de ejecución

El interprete interactivo ipython3.
Para ejecutar comandos en vivo y explorar.
Histórico editable de comandos
Autocompletar con el tabulador

Los scripts: cuando la cosa se complica,
mejor tener los comandos en un fichero de texto .py
y lanzarlos de golpe con el intérprete

Sintaxis

La sintaxis general: una línea por sentencia
La funcion print

Subsentencias, indentadas despues de los :
Ejemplos: def y if
Importante: indentar igual las subsentencias

Si abrimos {, [, (… podemos partir las líneas libremente hasta cerrarlos.

Comentarios: lo que venga detrás de # se ignora

Tipos de datos

Numéricos: 10 (int), 10.0 (float)

Textos: "Hola mundo" (str)

Un poco de listas: [1,2,'tres',4] (list)

Apenas mostramos diccionarios, booleanos…
Hoy los veremos

Manipulando datos

Operadores: * + - / // % ** in

Asignación = y actualización += *= ...

Llamar a funciones: round(importeFinal)

Llamar a métodos: palabra.count('a')

Indexado, rebanado y zancada: seq[i:r:z]

Formateo: f"Hola {nombre}"

¿Qué os costó entender más?

¿Y hoy qué?

Tomar decisiones

Recorrer estructuras

Usar librerías

Manipular ficheros

Ejercicio

Evitar conversaciones cruzadas en teléfono

Las graellas usan la definición de mesa

Complicado que cada cual se lo configure

Joan nos ha pasado un fichero con las mesas.

Tomatic las necesita en otro formato.

Decisiones

Booleanos (bool)

True y False

Sirven para tomar decisiones según lo que se encuentra el script

Es lo que da inteligencia al ordenador

¿Cómo los obtenemos?

Ya vimos los operadores in y not in

>>> 3 in [1,2,3]
True

Comparando

Operadores < <= == != >= >

>>> a = 3
>>> 1<a  # operador de inequalidad 'menor que'
True
>>> 10<a
False
>>> 1 <= a <= 10  # ¿del 1 al 10, ambos incluidos?
True
>>> 'alfredo' < 'benito'  # orden alfabético
True
>>> 'alfredo' == 'alfredo' # igualdad
True
>>> 'alfredo' != 'alfredo' # desigualdad
False

Sent. condicional if

# Espera que se lo escribamos por la consola
name = input("¿Quien Vive?\n")

if name == 'Carme':
    print("Hola Carme, bienvenida a casa")

Las subsentencias solo se ejecuta
si la condición se cumple

Sent. alternativa else

if name == 'Carme'
    print("Hola Carme, bienvenida a casa")
if not(name == 'Carme'):
    print("Fuera intruso!")

Se simplifica como:

if name == 'Aitor':
    print("Hola Aitor, bienvenida a casa")
else:
    print("Fuera intruso!")

Sent. alternativa elif

guests = "Adria Lucia Manel Vero".split()
if name == 'Carme':
    print("Hola Carme, bienvenida a casa")
else:
    if name in guests:
        print(f"Hola {name}, sientate, Carme no tardará en llegar")
    else:
        print("Fuera intruso!")
if name == 'Carme':
    print("Hola Carme, bienvenida a casa")
elif name in guests:
    print(f"Hola {name}, sientate, Carme no tardará en llegar")
else:
    print("Fuera intruso!")

Mútiples elif

guests = "Adria Lucia Manel Vero".split()
owner = "Carme"

name = input("¿Quien vive?\n")
if name == owner:
    print(f"Hola {owner}, bienvenida a casa")
elif name in guests:
    print(f"Hola {name}, sientate, {owner} no tardará en llegar")
elif name == "El de la luz":
    print("Deje de intentar timarme que estoy en Som Energia")
else:
    print("Fuera intruso!")

Operadores booleanos

and not or

Combinan booleanos y generan booleanos

pasaEsto or pasaAquello  # con que pase alguna pasa
                         # solo si no pasa ninguna no pasa

pasaEsto and pasaAquello # tienen que pasar las dos
                         # si una no pasa no pasa

not pasaEsto      # Pasa cuando no pasaEsto,
                  # y no pasa cuando pasaEsto

Operadores booleanos

a b a or b
True True True
True False True
False True True
False False False
a b a and b
True True True
True False False
False True False
False False False
a not a
True False
False True

Conversiones a bool

Cada tipo tiene valores ‘falsos’, el resto son ‘ciertos’

Ceros: 0, 0.0, 0j
Estructuras vacías: "" {} []
El no-objeto: None

alumnos = [ .... ]
# Estas condiciones serian equivalentes
if len(alumnos) != 0: ...    # bool False o True
if len(alumnos): ...         # int  0 o diferente de 0
if alumnos: ...              # list [] o llena

Recorridos
(Iteraciones)

Sentencia for

milista = [1, 2, 3, 4, 5, 6]

for numero in milista:    # Ojo, no olvides los dos puntos!
    print(f"El numero es {numero}")
    print(f"Su cuadrado es {numero*numero}")

numero es una variable que va adoptando los sucesivos valores de milista

Para cada valor se ejecutan las subsentencias.

Objetos recorribles

for recorre listas…
o cualquier cosa que contenga o genere elementos.

¿Textos? Pruébalo:

equipo = "tigres".upper()
for letra in equipo:
    print(f"Dame una {letra}")

print(f"¡¡¡{equipo}!!!")
¡Dame una 'T'!
¡Dame una 'I'!
¡Dame una 'G'!
¡Dame una 'R'!
¡Dame una 'E'!
¡Dame una 'S'!
¡¡¡TIGRES!!!

Intérvalos range

Genera secuencias de números

Sin contruir una lista: range(2000000)
no crea una lista de 2000000 numeros en memoria

for i in range(10):
    print(i)

# Similar a los [slices]
range(10) # 0 to 9
range(3,10) # 3 to 9
range(3,10,2) # 3, 5, 7, 9
range(10,3) # Empty
range(10,3,-2) # 10, 8, 6, 4
# Common operators
r = range(3,10,2) # 3, 5, 7, 9
len(r) # 4
7 in r # True
6 in r # False
6 not in r # True
r.index(7) # 2
r[2:] # 7, 9
list(r) # [3,5,7,9]

Filtros iteradores

>>> guests = ["Vane", "Sergi", "Albert", "Pau"]
>>> list(sorted(guests))
["Albert", "Sergi", "Pau", "Vane"]

>>> list(reversed(guests))
["Pau", "Albert", "Sergi", "Vane"]

>>> list(enumerate(guests))
[(0,"Vane"), (1,"Sergi"), (2,"Albert"), (3,"Pau")]

>>> gustos = ["fresas", "tomates", "alcachofas", "chocolate" ]
>>> list(zip(guests, gustos)])
[ ("Vane", "fresas"),
  ("Sergi", "tomates"),
  ("Albert", "alcachofas"),
  ("Pau", "chocolate"),
]

Listas del tirón

Construyendo una lista a partir de otra (o un iterable)

squares = []
for x in range(6):
    squares+=[x*x]
# [0, 1, 4, 9, 16, 25]
squares=[x*x for x in range(6)]

Filtrando elementos

l = [ x*x for x in range(6) if x % 3 ] # [0, 1, 4, 16, 25]

# Como se suele complicar, lo formateamos asi:
l = [
    (x, x*x, x*x*x)   # Trio: x, el cuadrado y el cubo
    for x in range(6)
    if x % 3
]

Generador del tiron

# Si en vez de [] usamos () no se crea una lista
# es un generador de elementos como el range
g = ( x*x for x in range(60000) if x % 3 )
# No generar listas intermedias acelera la ejecución

Ficheros

pathlib

Es una libreria estandard

Provee objetos Path

from pathlib import Path

p = Path('/home/user/.config')
home = Path.home() # el directorio de usuario
current = Path.cwd() # el directorio de trabajo
config = home / 'miconfig.ini' # navegación con operador /

Leer y escribir

csv = Path('mifichero.txt')

escrito = """\
Primera línea
Segunda línea
"""
csv.write_text(escrito, encoding='utf8')

leido = csv.read_text(encoding='utf8')
# "Primera línea\nSegunda línea\n"

Lectura progresiva

La forma para ficheros grandes, no podemos cargarlos todos en memoria.

with csv.open(encoding='utf8') as file:
    for nline, line in enumerate(file):
        print(f"Contenido de la línea {nline+1}: {line}")

Partes del path

p = Path('/home/vokimon/Documents/manual.tar.gz')
p.parts # ('/', 'home', 'vokimon', 'Documents', 'manual.tar.gz')
p.root # '/'
p.parent # '/home/vokimon/Documents'
p.parents # [ '/home/vokimon', '/home', '/' ]
p.name # 'manualdepython.tar.gz'
p.suffix # '.gz'
p.suffixes # ['.tar', '.gz' ]
p.stem # 'manualdepython.tar'
p.with_suffix('.zip') # Path('/home/vokimon/Documents/manual.zip')

Busqueda

# Todos los ficheros de texto del directorio actual
[x for x in Path(".").glob('*.txt')]

# Lo mismo pero mirando subdirectorios
[x for x in Path(".").glob('**/*.txt')] 

for child in Path('.').iterdir():
    ....

Propiedades

p.exists() # existe el path
p.is_dir() # es un directorio
p.is_file() # es un fichero
p.owner() # propietario del fichero

Operaciones

p.remove() # borra fichero
p.rmdir() # borra directorio vacio

p.mkdir(parents=True, exists_ok=true) # crea el directorio y los padres, si no existen
# si es un directorio mueve, sino mueve y renombra
p.rename("nuevonombre")

Ejercicio Mesas

Pasos

  • Obtener el contenido del fichero
  • Separarlo por líneas
  • Descartar líneas vacias
  • Descartar líneas de sala
  • Partir los nombres
  • Lista de listas de mesa
  • Escribir la salida

Obtener contenido del fichero

Pista: usar Path y read_text

Asegúrate de que el fichero está en el directorio de trabajo actual.

Separarlo por líneas

Hay que partir el texto por los '\n' (saltos de línea)

Pista: 'abracadabra'.split('a') partía el texto con las 'a'

Obtendremos una lista
cada elemento será una línia

Hacemos un for para las líneas y las imprimimos separadas

Filtrar las líneas vacias

Haremos un continue, si la linea esta vacía

Pista: el texto evalua falso cuando està vacio

Ojo: hay líneas no tan vacias, tienen espacios.
El método linea.strip() quita espacios delante y detras.

Filtrar los nombres de las salas

Haremos un continue también si la primera letra de línea es un '#'

Pregunta: ¿Lo miraremos antes o despues de mirar si la línea esta vacia?

Convertir las líneas en lista de nombres

Pista: Un split sin parametros, separaba palabras.

Enumerar las mesas

Podemos usar enumerate pero
¿Como saltamos las líneas ignoradas?

Iteramos para cada nombre

Acomulamos un texto de salida.

Un for dentro del for, para cada nombre en la linia.

Construimos el texto de salida

Escribim el fitxer de sortida

Usamos .with_suffix para obtener la ruta con la extensión cambiada.

Usamos write_text.

Huecos en los numeros de mesa

El enumerate se cuela las mesas que se salta.

Construyamos una lista intermedia que solo tenga mesas y enumeremos esa.

Para casa

Generar los grupos (salas)

groups:
  casademont:
    - carles
    - monica
    - nuri
    - marta
    - manel
    - carol
  infern:
    - 
  ...