Tallerista: Martín Gaitán (Phasety)
Colaboradores: Jairo Trad (Insus) y Julián Scortechini (Phasety)
Agosto/Septiembre de 2013
Python es un lenguaje de programación:
Guido trabaja en Dropbox, que está hecho en Python?
Aunque se cree que es un "lenguaje nuevo", la primera versión es de 1991, el mismo año de la estandarización de Fortran 90 y muy anterior a Java (1995)?
Google, Nasa y Walt Disney son algunas empresas/instuciones que usan Python como uno de sus principales lenguajes de programación?
sudo apt-get install ipython ipython-notebook spyder python-matplotlib python-numpy python-scipy python-sympy
Hay muchas maneras de usar el lenguaje Python. Cuando lo
Dijimos que es un lenguaje interpretado e interactivo. Si ejecutamos la consola (cmd.exe
) y luego python
, se abrirá la consola interactiva
En la consola interactiva podemos escribir sentencias o pequeños bloques de código que son ejecutados inmediatamente. Pero la consola interactiva estándar es fea. Mucho mejor usar IPython.
La consola IPython supera a la estándar en muchos sentidos. Podemos autocompletar (<TAB>
), ver ayuda rápida de cualquier objeto (?
) y muchas cosas más.
También podemos usar Python para hacer programas o scripts. Esto es, escribir nuestro código en un archivo con extensión .py y ejecutarlo con el interprete. Por ejemplo, el archivo hello.py (al que se le llama módulo) tiene este contenido:
print "Hello world!"
Si ejecutamos python scripts/hello.py
se ejecutará n el interprete Python y obtendremos el resultado
!python scripts/hello.py
Hello world!
Python no exige un editor específico y hay muchos modos y maneras de programar.
Un buen editor es Spyder que es un IDE (entorno integrado: editor + ayuda + consola interactiva)
Podés ejecutar todo el módulo en la consola interactiva que incluye, o seleccionar una porción.
Y otra forma muy útil es usar los Notebooks. El notebook es un entorno interactivo y enriquecido. Podemos crear y editar "celdas" código Python que podés editar y volver a ejecutar, podés intercalar celdas de texto, fórmulas matemáticas, y hacer que gráficos se muestren inscrutados en la misma pantalla. etc. Estos archivos se guardan con extensión .ipynb, que pueden exportarse como html (estáticos) o como código python puro. (.py)
Los notebooks son muy útiles para la "programación exploratoria", muy frecuente en ciencia e ingeniería. El Dr. Damián Ávila (amigo de la casa) los llamó "Papers ejecutables".
Todo el material de estos cursos estarán en formato notebook.
Python es un lenguaje de muy alto nivel y por lo tanto trae muchos tipos de datos incluidos.
1 + 1.4 - 12
-9.6
Ejecuten su consola y ¡a practicar!
2**3
8
4564654654654654122453453545544545467100003456970149034538478541340012 % 2
0L
Los tipos numéricos básicos son integer (enteros sin limite), float (reales) y complex (complejos)
(3.2 + 12j) * 2
(6.4+24j)
Las operaciones aritméticas básicas son:
+
-
*
/
%
**
//
Las operaciones se pueden agrupar con parentesis y tienen precedencia estándar
resultado = int((21 + 4.5)**2 / 1.2) + 1j
print(resultado)
(541+1j)
Atenti: El modo interactivo print
imprime el resultado pero no devuelve in output. int()
es el tipo de datos para enteros y permite forzar que el resultado sea de este tipo (truncado).
El resultado quedó guardado en la variable resultado
.
type(resultado)
complex
En Python todo es un objeto. Los objetos no solo guardan valores sino que que tienen métodos. Es decir, traen "funciones" (acciones) que podemos ejecutarles. Prueben escribir resutado.
y apretar <TAB>
. Por ejemplo:
resultado.conjugate()
(541-1j)
En una sesión interactiva el ?
brinda ayuda contextual
resultado.conjugate?
magnitud
con un valor realintensidad
cuya parte real sea 1.5 veces magnitud
y la parte imaginaria 0.3 veces magnitud
+ 1jintensidad
Una cadena o string es una secuencia de caracteres (letras, números, simbolos). Hay dos tipos str y unicode.
print ("Hola mundo!")
Hola mundo!
chinito = u'字漢字'
type(chinito)
unicode
Se pueden definir con apóstrofes, comillas, o triple comillas
calle = "O'Higgings"
"""Me gustas cuando callas
porque estás como ausente..."""
'Me gustas cuando callas\nporque est\xc3\xa1s como ausente...'
poema = _
Las cadenas tienen sus propios métodos: pasar a mayúsculas, capitalizar, reemplazar una subcadena, etc.
print poema.replace('gustas', 'molestas').replace('callas', 'hablas').replace('como ausente', 'demasiado gritona')
Me molestas cuando hablas porque estás demasiado gritona...
calle.lower()
"o'higgings"
También se pueden concatenar
calle + " fue un soldado de San Martín"
"O'Higgings fue un soldado de San Mart\xc3\xadn"
Pero las cadenas también son secuencias. O sea, conjuntos ordenados que se pueden indizar, recortar, reordenar, etc.
cadena = "SLICEINDEX"
print len(cadena)
print cadena[0]
print cadena[1]
print cadena[0:5]
10 S L SLICE
cadena[:3], cadena[-2:], cadena[:-5]
('SLI', 'EX', 'SLICE')
cadena[::2] #wow!
'SIENE'
Algunas operaciones
"*" * 80 # repetición
'********************************************************************************'
"%s era un groso" % calle # interpolacion
"O'Higgings era un groso"
Pero el tipado es fuerte. En general, los tipos no se convierten implícitamente
"1.2" + 2
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-23-67bb27a69e2f> in <module>() ----> 1 "1.2" + 2 TypeError: cannot concatenate 'str' and 'int' objects
float("1.2") + 2
3.2
nombres = ["Meli", "Nadia", "Daniel"]
type(nombres)
list
Las listas tambien son secuencias
nombres[0]
'Meli'
nombres[-2:]
['Nadia', 'Daniel']
Y pueden contener cualquier tipo de objetos
mezcolanza = [1.2, "Jairo", 12e6, calle, nombres[1]]
print mezcolanza
[1.2, 'Jairo', 12000000.0, "O'Higgings", 'Nadia']
Hasta acá son iguales a las tuplas
una_tupla = ("Martín", 1.2, (1j, nombres[0]))
print type(una_tupla)
print una_tupla[1:3]
<type 'tuple'> (1.2, (1j, 'Meli'))
LA DIFERENCIA es que las listas son mutables. Es decir, es un objeto que puede cambiar: extenderse con otra secuencia, agregar o quitar elementos, cambiar un elemento o una porción por otra, reordenarse in place, etc.
mezcolanza.append(una_tupla)
print mezcolanza
[1.2, 'Jairo', 12000000.0, "O'Higgings", 'Nadia', ('Mart\xc3\xadn', 1.2, (1j, 'Meli'))]
nombres.sort()
print nombres
['Daniel', 'Meli', 'Nadia']
mezcolanza[-1] = "otra cosa"
print mezcolanza
[1.2, 'Jairo', 12000000.0, "O'Higgings", 'Nadia', 'otra cosa']
Las tuplas son mucho más eficientes (y seguras) si sólo vamos a leer elementos (como una memoria ROM). Pero muchas cosas son comunes.
l = [1, 3, 4, 1]
t = (1, 3, 1, 4)
l.count(1) == t.count(1)
True
t[1] = "osooooo"
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-48-aee0fef3e8f2> in <module>() ----> 1 t[1] = "osooooo" TypeError: 'tuple' object does not support item assignment
Como toda secuencia, las listas y tuplas se pueden desempacar
nombre, nota = ("Juan", 10)
print nota
10
a, b = 1, 2.0
a, b = b, a
print a
2.0
Como con los números, se pueden convertir las secuencias de un tipo de dato a otro. Por ejemplo:
a = (1, 3, 4)
b = list(a)
print b, type(b)
[1, 3, 4] <type 'list'>
Una función builtin muy útil es range
help(range)
Help on built-in function range in module __builtin__: range(...) range([start,] stop[, step]) -> list of integers Return a list containing an arithmetic progression of integers. range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0. When step is given, it specifies the increment (or decrement). For example, range(4) returns [0, 1, 2, 3]. The end point is omitted! These are exactly the valid indices for a list of 4 elements.
range(6)
[0, 1, 2, 3, 4, 5]
range(-10, 10)
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 25, 5)
[0, 5, 10, 15, 20]
También hay una función estándar que da la sumatoria
help(sum)
Help on built-in function sum in module __builtin__: sum(...) sum(sequence[, start]) -> value Returns the sum of a sequence of numbers (NOT strings) plus the value of parameter 'start' (which defaults to 0). When the sequence is empty, returns start.
sum([1, 5, 5, 10])
21
En toda secuencia tenemos el método index, que devuelve la posición en la que se encuentra un elemento
a = [1, 'hola', []]
print a.index('hola')
1
y como las listas son mutables también se pueden reordenar inline (no se devuelve un valor, se cambia sobre la misma variable)
a.reverse()
print a
[[], 'hola', 1]
La forma alternativa es usando una función, que devuelve un valor
a = list(reversed(a))
print a
[[], 'hola', 1]
Nota: se fuerza la conversión de tipo con list()
porque reversed no devuelve estrictamente una lista. Ya veremos más sobre esto.
Una función útil es zip()
, que agrupa elementos de distintas secuencias
nombres = ['Juan', 'José', 'Cismondi']
pasiones = ['cerveza', 'visitar phasety', 'lechuga']
zip(nombres, pasiones)
[('Juan', 'cerveza'), ('Jos\xc3\xa9', 'visitar phasety'), ('Cismondi', 'lechuga')]
En todo lenguaje necesitamos controlar el flujo de una ejecución segun una condición Verdadero/Falso (booleana). Si (condicion) es verdadero hacé (bloque A); Sino hacé (Bloque B). En pseudo código:
Si (condicion):
bloque A
sino:
bloque B
y en Python es muy parecido!
edad = 17
if edad < 18:
print "Usted es menor de edad. Raje de acá, pendejo"
else:
print "Bienvenido, jovato"
Usted es menor de edad. Raje de acá, pendejo
Los operadores lógicos en Python son muy explicitos.
A == B
A > B
A < B
A => B
A =< B
A != B
A in B
y a todos los podemos combinar con not
, que niega la condición
Podemos tener multiples condiciones. Se ejecutará el primer bloque cuya condición sea verdadera, o en su defecto el bloque else
. Esto es equivalente a la sentencia switch
de otros lenguajes
if edad < 12:
print "Feliz dia del niño"
elif 13 < edad < 18:
print "Qué problema los granitos, no?"
elif edad in range(19, 60):
print "No seré feliz pero tengo marido"
else:
print "Y eso es todo amigos!"
Qué problema los granitos, no?
En un if
, la conversión a tipo boolean es implícita. El tipo None
(vació), el 0
, una secuencia (lista, tupla, string) (o conjunto o diccionario, que ya veremos) vacía siempre evalua a False
. Cualquier otro objeto evalua a True
.
if 5 - 5:
a = "No es cero"
else:
a = "Dio cero"
print a
Dio cero
Para hacer asignaciones condicionales se puede usar la estructura ternaria del if
: A si (condicion) sino B
a = "No es cero" if (5-5) else "dio cero"
print a
dio cero
Otro control es iterar sobre una secuencia (o "iterador"). Obtener cada elemento para hacer algo. En Python se logra con la sentencia for
sumatoria = 0
for elemento in [1, 2, 3]:
sumatoria = sumatoria + elemento
print sumatoria
6
Notar que no iteramos sobre el índice de cada elemento, sino sobre los elementos mismos. ¡Basta de i
, j
y esas variables innecesarias! . Si por alguna razon son necesarias, tenemos la función enumerate
for posicion, valor in enumerate([4, 3, 19]):
print "El valor de la posicion %s es %d" % (posicion, valor)
El valor de la posicion 0 es 4 El valor de la posicion 1 es 3 El valor de la posicion 2 es 19
El bloque for
se corre hasta el final del iterador o hasta encontrar un sentencia break
sumatoria = 0
for elemento in range(1000):
if elemento > 100:
break
sumatoria = sumatoria + elemento
print sumatoria, elemento
5050 101
También podemos usar continue
para omitir la ejecución de "una iteración"
sumatoria = 0
for elemento in range(20):
if elemento % 2:
continue
print elemento
sumatoria = sumatoria + elemento
print "sumatoria pares:", sumatoria
0 2 4 6 8 10 12 14 16 18 sumatoria pares: 90
Muchas veces queremos iterar una lista para obtener otra, con sus elementos modificados. Por ejemplo, obtener una lista con los cuadrados de los primeros 10 enteros.
cuadrados = []
for i in range(10):
cuadrados.append(i**2)
print cuadrados
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Una forma compacta y elegante (¡pythónica!) de escribir esta estructura muy frecuente son las listas por comprehensión:
[i**2 for i in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Se lee: "Obtener el cuadrado de cada elemento i de la secuencia (rango 0 a 9)". Pero además podemos filtrar: usar solo los elementos que cumplen una condición. Por ejemplo, obtener la sumatoria de los cubos de los numeros impares menores a 10. 9∑a=0a3∣a impar
sum([a**3 for a in range(10) if a % 2 == 1])
1225
La misma sintaxis funciona sin tener que hacer listas: se crean generadores, que son tipos de datos iterables pero no indizables (es el mismo tipo de objeto que devuelve reversed
, que ya vimos). Si es para recorrer todos los elementos de corrido son mucho más eficientes.
(a**3 for a in range(10) if a % 2 == 1)
<generator object <genexpr> at 0x3420910>
sum(a**3 for a in range(10) if a % 2 == 1)
1225
Otro tipo de sentencia de control es while: iterar mientras se cumpla una condición
a = 0
while a < 10:
print a
a += 1
0 1 2 3 4 5 6 7 8 9
"3,4 1,2 -6 0 9,7"
. Podes usar el método split()
para separa la cadena por espacios en blanco
La diccionarios son otro tipo de estructuras de alto nivel que ya vienen incorporados. A diferencia de las secuencias, los valores no están en una posición sino bajo una clave: son asociaciones clave:valor
camisetas = {'Orión': 1, 'Martinez': 7, 'Riquelme': 10, 'Gaitán': 'Jugador nº 12'}
Accedemos al valor a traves de un clave
camisetas['Riquelme']
10
Las claves pueden ser cualquier objeto inmutable (cadenas, numeros, tuplas) y los valores pueden ser cualquier tipo de objeto. Las claves no se pueden repetir pero los valores sí.
Importante: los diccionarios no tienen un orden definido. Si por alguna razón necesitamos un orden, debemos obtener las claves, ordenarlas e iterar por esa secuencia de claves ordenadas.
sorted(camisetas.keys())
['Gait\xc3\xa1n', 'Martinez', 'Ori\xc3\xb3n', 'Riquelme']
Los diccionarios son mutables. Es decir, podemos cambiar el valor de una clave, agregar o quitar.
camisetas['Blandi'] = 9
print camisetas
{'Ori\xc3\xb3n': 1, 'Martinez': 7, 'Blandi': 9, 'Riquelme': 10, 'Gait\xc3\xa1n': 'Jugador n\xc2\xba 12'}
Hay muchos métodos útiles
for jugador, camiseta in camisetas.items():
if jugador == 'Gaitán':
continue
print "%s lleva la %d" % (jugador, camiseta)
Orión lleva la 1 Martinez lleva la 7 Blandi lleva la 9 Riquelme lleva la 10
Se puede crear un diccionario a partir de tuplas (clave, valor)
con el construcutor dict()
dict([('Yo', 'gaitan@phasety.com'), ('Meli', 'mgomez@phasety.com'), ('Cismondi', 'cismondi@phasety.com')])
{'Cismondi': 'cismondi@phasety.com', 'Meli': 'mgomez@phasety.com', 'Yo': 'gaitan@phasety.com'}
Que es muy útil usar con la función zip()
que ya vimos
Los diccionarios son super útiles, recuerdenlos!
Los conjuntos (set()
) son grupos de claves únicas e inmutables.
mamiferos = {'perro', 'gato', 'leon'}
domesticos = {'perro', 'gato', 'gallina'}
aves = {'gallina', 'halcón', 'colibrí'}
mamiferos.intersection(domesticos)
{'gato', 'perro'}
mamiferos.union(domesticos)
{'gallina', 'gato', 'leon', 'perro'}
aves.difference(domesticos)
{'colibr\xc3\xad', 'halc\xc3\xb3n'}