La computadora es una gran gran calculadora que permite hacer cualquier tipo de cuenta de las que necesitemos dentro de nuestras carreras (y de la vida también) mientras sepamos cómo decirle a la máquina qué cómputos hacer.
La idea de esta primera clase, es comprender los siguiente conseptos:
Para hacer estas cuentas la computadora tiene que almacenar los números que necesitemos y luego hacer las operaciones con ellos. Nuestros valores numéricos se guardan en espacios de memoria, y esos espacios tienen un nombre, un rótulo, con el cual los podremos llamar y pedirle a la computadora que los utilice para operar con ellos, modificarlos, etc. Ese nombre a cada espacio de memoria se asigna, al menos en Python, con el símbolo =, que suele llamarse operador de asignación.
Pero no solamente guardaremos valores numéricos. Además, como veremos ahora, podemos guardar otros tipos de datos, como texto (strings) y listas (lists) entre muchos otros. Todos los tipos de valores que podremos almacenar difieren entre si en el espacio en memoria que ocupan y las operaciones que podremos hacer con ellos. A estos tipos de datos con los que se puede operar se los llama objetos.
Veamos un ejemplo
In [9]:
x = 5
un_texto = 'Hola mundo!' # Esto es un comentario
una_lista = [1,2,3, "texto"]
Acá asignamos al espacio en memoria x
el valor 5 que es un objeto de tipo int (número entero). A estos espacios de memoria se los suele llamar variables. De modo que en este caso tenemos la variable x
que guarda el valor 5.
Además, creamos otras dos variables: un_texto
y una_lista
. Con los valores 'Hola mundo!' y [1,2,3] respectivamente.
Los valores entre comillas simples (') o dobles (") son objetos del tipo string, que es la forma en que se le dice a la computadora que se busca almacenar una palabra. Y los valores entre corchetes son listas, que básicamente son conjuntos de otros valores. En nuestro ejemplo armamos una lista con los valores numéricos 1, 2, 3 y con un valor del tipo string "texto".
Cualquier texto escrito luego de #
Python lo toma como un comentario y no lo lee. Esto está muy bueno para dejar anotaciones. Úsenlo!
Ahora que tenemos las variables ya declaradas podemos operar con funciones sobre ellas. Las funciones son un conjunto de lineas de código que realizan alguna tarea específica. Por ejemplo, veamos las funciones print()
y type()
.
In [10]:
print(x)
print(type(x))
print(un_texto, "es del tipo", type(un_texto))
Como vemos, la función print()
nos permite mostrar en pantalla el valor de una variable en específico.
Por otro lado, la función type()
nos retorna el tipo de objeto almacenado en una variable. Donde 'int' hace alusión a los números enteros y 'str' a los strings. Esta función es muy útil para entender con qué tipo de variables estamos trabajando.
Además, vemos que print()
, por ejemplo, puede recibir más de un objeto como argumento. Pasa lo mismo con type()
?
In [11]:
print(type(x, un_texto))
No, no pasa lo mismo. En este caso nos dice que solo puede recibir 1 o 3 argumentos. Donde los otros dos argumentos son para cuestiones más específicas que no nos van a interesar por ahora.
Para los objetos que son números los operadores +
, -
, *
, /
y **
funcionan exactamente como los operadores matemáticos que conocemos. Por ejemplo:
In [12]:
x = 10
y = 8
print('La suma de x e y es igual a', x + y, '. Y el resultado es de tipo', type(x+y))
print('La resta de x e y es igual a', x - y, '. Y el resultado es de tipo', type(x-y))
print('La división entre x e y es igual a', x / y, '. Y el resultado es de tipo', type(x/y))
print('La multiplicación entre x e y es igual a', x * y, '. Y el resultado es de tipo', type(x*y))
print('x elevado a la y es igual a', x ** y, '. Y el resultado es de tipo', type(x**y))
La computadora no almacena todos los números de igual forma, ya que intenta ahorrar espacio, es por esto vemos que la mayoría de los resultados anteriores nos dan todos números enteros (que ocupan menos lugar en memoria).
La única excepción es la división. Vemos que para este caso el resultado es de tipo flotante (float), lo que significa que es un número con coma. En general Python convierte los números a flotantes antes de realizar cualquier operación que pueda dar como resultado un número con coma $^1$. En caso de que queramos realizar la división entera (10 "dividido" 8 = 1) podemos utilizar el operador //. Y en caso que queramos declarar directamente un número con coma podemos poner x = 10.0
Por completitud, el último tipo de número que falta es el imaginario. Que se declaran de la siguiente manera:
num_complejo = 24 + 10j
i = 0 + 1j
De forma nativa Python utiliza j en vez de i porque es la letra más usual en ingeniería en las universidades estadounidenses.
$\scriptsize\text{1. En realidad esto es así a partir de la versión 2.7. de Python}$
Antes de continuar vamos a ver un poco cómo dar formato a los strings. Esto nos interesa para poder imprimir cosas en pantalla de una forma más ordenada y elegante, pero además es muy útil para manejar instrumentos en laboratorio, ya que estos reciben como input una cadena de texto.
En el ejemplo anterior para imprimir un texto, junto a los valores de las variables, realizamos líneas de este estilo
print("La suma de x e y es igual a", x + y, ". Que es de tipo", type(x+y))
que en principio no tienen nada de malo, pero son difíciles de leer en el código y pueden escalar muy feo si hay que poner más variables.
Dicho esto, veamos algunas formas de reescribir estas lineas de forma más elegante
In [13]:
print('Ejemplo 1:')
print(f'La suma de x e y es igual a {x} + {y}. Que es de tipo {type(x+y)}') # Solo en versiones >= 3.6
print('\nEjemplo 2:')
print('La suma de x e y es igual a {} + {}. Que es de tipo {}'.format(x, y, type(x+y)))
print('\nEjemplo 2 bis:')
print('La suma de x e y es igual a {1} + {2}. Que es de tipo {0}'.format(type(x+y), x, y))
print('\nEjemplo 3:')
print('La suma de x e y es igual a %d + %d. Que es de tipo %s' % (x, y, type(x+y)))
...
En el ejemplo 1 vemos una forma muy elegante y fácil de leer que se conoce como f-string (format string). Esta forma está muy buena, pero solo es compatible con las versiones de Python a partir de la 3.6, y muchas computadoras, con sistemas operativos más antiguos, tienen por defecto la versión 3.5.2.
En el ejemplo 2 y 2 bis vemos otra forma, también bastante concisa y elegante. Esta es la recomendada para programas que van a compartir con otra gente, ya que es compatible con todas las versiones de Python. La idea en general es poner {} donde van las variables y luego listarlas al final como argumentos del .format()
. En el bis vemos que también es posible listarlas desordenadas y utilizar números entre las llaves para elegir el orden deseado.
En el ejemplo 3 vemos una forma un tanto distinta, donde hay que aclarar el tipo de variable con la que estamos trabajando (%d hace alusión a digit y %s a string). Este formato es bastante molesto de usar pero tiene la ventaja de que es común a otros muchos lenguajes de programación como C, C++ y Java. La idea de declarar el tipo de variable viene de lenguajes que necesitan compilar. Acá un link si alguien quiere adentrarse más en la diferencia entre lenguajes interpretados (Python, Javascript, ...) y lenguajes compilados (C, Fortran, ...).
Aclaración: vemos un \n
en algunos de los textos. Estos simplemente lo que hace es poner una linea en blanco en el medio. Si lo probás por cuenta propia rápidamente vas a entender cómo funciona.
Encontrar las raíces de el polinomio
$$x^2 + \frac{3}{2}x -1$$Recordar que la fórmula para las raíces de un polinomio del estilo $a\,x^2 + b\,x + c$ es
$$x_{1,2} = \frac{-b\pm(b^2 -4ac)^{\frac{1}{2}}}{2a}$$¿Los resultados son de tipo int o float?
Ejercicio 1 bis: Reemplazar los valores del Ejercicio 1 para encontrar las raíces de
$$4x^2 - x$$Las listas son cadenas de datos de cualquier tipo, unidos por estar en una misma variable, con posiciones dentro de esa lista, con las cuales nosotros podemos llamarlas.
lista[0]
lista[3]
, por lo mismo que lo anteriorlista[-1]
, para el penúltimo es lista[-2]
, y así...lista[i-1:j]
(probando va a quedar más claro!)Veamos algunos ejemplos de todo esto
In [14]:
lista1 = [1,2,3, "texto"]
print(lista1, type(lista1))
print(lista1[0], type(lista1[1]))
print(lista1[2], type(lista1[2]))
print(lista1[-1], type(lista1[-1]))
print(lista1[1:3], type(lista1[1:3]))
In [15]:
lista2 = [15, 10]
lista3 = [12, 17]
print(lista2 + lista3)
print(lista2[1] + lista3[0])
In [16]:
a = 10
b = a**2
c = a**3
lista4 = [a, b, c]
print(lista4)
In [17]:
lista = [1, 4, 7]
print(lista)
lista.append(9)
print(lista)
In [18]:
obj_range = range(10)
lista_range = list(obj_range)
print(obj_range, type(obj_range))
print(lista_range, type(lista_range))
La fución range()
nos da un objeto del tipo range que define una sucesión de números en abtracto, y podemos convertirlos a una lista de números explícitos, si lo necesitamos, utilizando list()
(esto de convertir un objeto en otro vamos a poder hacerlo casi siempre entre objetos similares y durante el curso vamos a ver otros ejemplos).
Tener en cuenta que el objeto range(0, 10), por ejemplo, nos da la succesión de elemenentos del 0 incluído hasta el 10 excluído. Esto es así para que la función range(10) nos de 10 elementos y no 11.
Siempre que se quiera conocer más de de una función, o de cualquier otro objeto en Python, se puede utilizar el comando help(lo_que_quiero_conocer)
.
Como ejemplo, en este caso sería help(range)
.
No lo ponemos en la presentación porque el texto de salida es largo. Pero si lo prueban en sus computadoras van a ver que en una de las primeras lineas les dice
| range(stop) -> range object
| range(start, stop[, step]) -> range object
lo que significa que no solo podemos darle el argumento stop como valor, sino que, opcionalmente, también podemos darle un inicio y un paso. Veamos dos ejemplos:
In [19]:
print(list(range(10, 15)))
print(list(range(0, 10, 2)))
Para verificar si se cumplen ciertas condiciones vamos a introducir un nuevo tipo de variables: los bool. Esta variable solo puede tomar dos valores posibles: 1
y 0
, o True
y False
.
Estas variables se comportan bajo el álgebra de bool, por lo que permiten hacer operaciones lógicas si se está familiarizado, pero lo que nos es relevante para este momento del curso es que nos permiten verificar igualdades.
Por ejemplo, si le preguntamos a Python si 5 es mayor que 4 nos va a devolver el valor True
In [20]:
print(5 > 4)
El símbolo >
es lo que se llama un operador lógico. Acá una lista de los más usados:
a>b
Verifica si a es mayor que ba<b
Verifica si a es menor que ba>=b
Verifica si a es mayor o igual que ba<=b
Verifica si a es menor o igual que ba==b
Verifica si a es igual que ba!=b
Verifica si a es distinto que bTener en cuenta que para verificar la igualdad se usa ==
y no =
ya que este último está reservado para asignar un valor.
In [21]:
print(5 > 4)
print(5 < 4)
print(5 == 4)
print(5 != 4)
print(type(4 > 5))
In [22]:
num1 = -3
num2 = 4
if num1 > 0:
print('Este número es positivo:')
print(num1)
if num2 > 0:
print('Este número es positivo:')
print(num2)
Vemos que la estructura de cada uno de losif
es algo así:
esto se ejecuta sí o sí
if BOOL:
|esto se ejecuta
|si BOOL es True
esto se ejecuta sí o sí
En el código de arriba se están leyendo ambos if
, pero el primero tiene una condición que retorna False
por lo que no ejecuta el código que tiene adentro. En cambio, la condición del segundo es True
, por lo que sí se ejecutan los print().
Las barritas marcan que esa parte de código está identada, lo que implica que es una parte que se va a ejecutar únicamente si la condición que está arriba (y sin identar) se cumple.
Pero qué pasa si también quiero printear los números que no son positivos? Para eso existe else
, que te permite ejecutar un bloque de código cuando la condición no se cumple, por ejemplo:
In [23]:
if num1 > 0:
print('Este número es positivo:')
print(num1)
else:
print('Este número no es positivo')
print(num1)
Bien, pero qué pasa si el número no es ni positivo ni negativo? Sino que es 0. Para resolver este problema existe elif
que es una abreviación de else if que nos permite agregar más de una condición. En este caso:
In [24]:
num = -3
if num > 0:
print('El número es positivo:')
print(num1)
elif num < 0:
print('El número es negativo')
print(num1)
else:
print('El número es cero')
Por lo que la estructura completa queda:
esto se ejecuta sí o sí
if BOOL_1:
|esto se ejecuta
|si BOOL_1 es True
elif BOOL_2:
|esto se ejecuta
|si BOOL_1 es False
|pero BOOL_2 es True
elif BOOL_3:
|esto se ejecuta
|si BOOL_1 es False,
|si BOOL_2 es False
|pero BOOL_3 es True
...(elif BOOL_4 etc)
else:
|esto se ejecuta
|si todas las BOOL_n
|son False
esto se ejecuta sí o sí
Realizar un pequeño programa en el que verifique si un número es par. Para esto, pueden verificar si el resto al dividirlo por 2 es cero.
Luego, si el número es par, que lo imprima en pantalla. Si no lo es que imprima el siguiente número par.
La idea es que tengan un resultado similar al siguiente:
input: 4
output: El número es par.
input: 5
output: El número es impar.
El siguiente número par es 6
Ayuda: El operador a%b
te da el resto de dividir a entre b
Supongamos ahora que queremos realizar este proceso que hicimos antes para los para los primeros 10 números enteros positivos. Vemos que es muy trabajoso hacerlo a mano. Y en general los programas los vamos a realizar sobre muchísimos más elementos que 10.
Para solucionar este problema podemos utilizar los ciclos de Python, que nos permiten ejecutar un código muchas veces sobre distintos elementos. Veamos un ejemplo del ciclo for
. Para esto, vamos a crear una lista con algunos números, vamos a recorrer sobre ella, vamos a sumarle 0.5 a cada uno de los elementos y vamos a printearlos.
In [25]:
lista = [1, 6, 8, 9]
for elemento in lista:
print(elemento + 0.5)
Al proceso de recorrer los elementos de un objeto se lo llama iterar.
En este caso estamos iterando sobre una lista, pero el for
nos permite iterar sobre varios objetos, como por ejemplo el range.
In [26]:
for i in range(3):
print(i)
De este modo podemos reproducir el resultado anterior usando el range.
In [27]:
for i in range(len(lista)):
print(lista[i] + 0.5)
Que parece más engorroso pero es útil si te interesa conocer el número del elemento con el que estás trabajando, ya que el i guarda ese valor, por ejemplo
In [28]:
lista = [1, 6, 8, 9]
for i in range(len(lista)):
print(f'El elemento {i} de las lista es {lista[i]}')
Ya vimos algunas funciones, como print()
, range()
, type()
o help()
.
Pero supongamos que quiero una función que haga otra cosa, como calcularme el promedio entre los dos números.
En este caso no conocemos ninguna función que haga eso, así que vamos a crearla. Para esto utilizamos las palabra def
y return
.
In [29]:
def promedio(num1, num2):
suma_de_numeros = num1 + num2
promedio = suma_de_numeros / 2
return promedio
de este modo declaramos la función promedio. La estructura en general es
def nombre_de_la_funcion(par_1, par_2, par_3, ...):
|bloque de código
|return algun_valor
donde _par_1, par2, ... son los parámetros de la función.
Ahora podemos llamar a esta función de el mismo modo que hacíamos con las demás:
In [30]:
print(promedio(2, 6))
print(promedio(5, 5))
print(promedio(3, 4))
La función anterior se puede escribir de forma mucho más corta si se quiere:
def promedio(num1, num2):
return (num1 + num2) / 2
o inclusive
def promedio(num1, num2): return (num1 + num2) / 2
pero si el proceso es largo y no trivial es muy recomendado dividir los pasos e ir dejando comentarios para que el código sea más legible.
En Python existe otra forma de declarar funciones además de la que vimos anteriormente, que es utilizando la palabra lambda
. La función anterior de este modo se escribiría de la siguiente forma
promedio = lambda num1, num2: (num1 + num2) / 2
Esta forma está en desuso para declarar funciones en un linea, ya que es menos legible que la anterior. Pero es útil saberla para otro tipo de usos o por si se la encuentran leyendo el código de otra persona.
Para más referencias pueden googlear. Dejamos algunas de referencia:
http://pybonacci.org/2012/06/07/algebra-lineal-en-python-con-numpy-i-operaciones-basicas/
http://relopezbriega.github.io/blog/2015/06/14/algebra-lineal-con-python/
http://pendientedemigracion.ucm.es/info/aocg/python/modulos_cientificos/numpy/index.html
Pero es importantísimo manejarse con la documentación de las bibliotecas que se utilizan
https://docs.python.org/2/library/math.html
http://docs.scipy.org/doc/numpy/reference/routines.linalg.html
http://matplotlib.org/api/pyplot_api.html
Para seguir profundizando con la programación en Python, ofrecemos distintos recursos
Un tutorial: http://www.learnpython.org/
How to think like a computer scientist (aprendizaje interactivo): http://interactivepython.org/runestone/static/thinkcspy/index.html
Otro tutorial, en inglés, pero muy completo: http://learnpythonthehardway.org/book
Coursera, que nunca está de más: https://www.coursera.org/learn/interactive-python-1
Otro más: https://es.coursera.org/learn/python
Y por fuera del taller, seguimos en contacto. Tenemos un grupo de Facebook donde pueden hacerse consultas y otros chicos que fueron al taller antes o aprendieron por sus medios podrán responderles. El grupo es https://www.facebook.com/groups/303815376436624/?fref=ts
Tenemos otro taller dentro de una semana donde profundizaremos el uso de algunas herramientas clave para el análisis de datos de Laboratorio. ¡No te lo podés perder!
A parte, en nuestro Github (https://github.com/fifabsas/talleresfifabsas) iremos colgando nuevo material para ejemplificar, con problemas de las materias de Física resueltos numéricamente, herramientas de Labo y mucho más.
Todo esto es posible gracias al aporte de mucha gente.