Qué es programar?

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:

  • Cómo definir variables y las verlas en pantalla.
  • Cómo hacer cuentas con las variables.
  • Trabajar con listas.
  • Condicionales (if).
  • Reiterar acciones (for loops).
  • Declarar fuciones

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!

Funciones predefinidas

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))


5
<class 'int'>
Hola mundo! es del tipo <class 'str'>

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))


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-07485e09eb8c> in <module>
----> 1 print(type(x, un_texto))

TypeError: type() takes 1 or 3 arguments

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.

Operadores y tipos de números

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 suma de x e y es igual a 18 . Y el resultado es de tipo <class 'int'>
La resta de x e y es igual a 2 . Y el resultado es de tipo <class 'int'>
La división entre x e y es igual a 1.25 . Y el resultado es de tipo <class 'float'>
La multiplicación entre x e y es igual a 80 . Y el resultado es de tipo <class 'int'>
x elevado a la y es igual a 100000000 . Y el resultado es de tipo <class 'int'>

Tipos de números: enteros, flotantes e imaginarios

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}$

Inciso: Cómo dar formato al texto

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)))


Ejemplo 1:
La suma de x e y es igual a 10 + 8. Que es de tipo <class 'int'>

Ejemplo 2:
La suma de x e y es igual a 10 + 8. Que es de tipo <class 'int'>

Ejemplo 2 bis:
La suma de x e y es igual a 10 + 8. Que es de tipo <class 'int'>

Ejemplo 3:
La suma de x e y es igual a 10 + 8. Que es de tipo <class 'int'>

...

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.


Ejercicio 1

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}$$
Objetivos
  • Declarar las variables $a$, $b$, $c$ para los coeficientes
  • Guardar los resultados en las variables $x1$ y $x2$.
  • Imprimir los valores de $x1$ y $x2$ aclarando cuál es cada uno usando alguna de las formas que vimos para formatear texto.

¿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$$

Listas

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.

Cosas a conocer sobre las listas

  • En Python, las listas se enumeran desde el 0 en adelante. De modo que el primer elemento es el lista[0]
  • Para pedir, por ejemplo, el elemento 4 ponemos lista[3], por lo mismo que lo anterior
  • Para pedir el último elemento de una lista la sintaxis es lista[-1], para el penúltimo es lista[-2], y así...
  • Para obtener todos los elementos de una lista entre el valor i-ésimo y el j-ésimo la sintaxis es lista[i-1:j] (probando va a quedar más claro!)
  • Sumar listas las concatena. No suma elemento a elemento como si fuera un vector (eso lo veremos más adelante en el curso).
  • Se puede guardar el valor de una variable dentro de una lista sin ningún problema.

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]))


[1, 2, 3, 'texto'] <class 'list'>
1 <class 'int'>
3 <class 'int'>
texto <class 'str'>
[2, 3] <class 'list'>

In [15]:
lista2 = [15, 10]
lista3 = [12, 17]
print(lista2 + lista3)
print(lista2[1] + lista3[0])


[15, 10, 12, 17]
22

In [16]:
a = 10
b = a**2
c = a**3
lista4 = [a, b, c]
print(lista4)


[10, 100, 1000]

Agregar elementos a una lista

En la sección de strings vimos el .format(), que es una función que se aplica sobre los tipos de variables de texto. Otra función de esta índole es el .append(). Esta función se puede utilizar sobre una lista para agregar un elemento a ella. Por ejemplo:


In [17]:
lista = [1, 4, 7]
print(lista)
lista.append(9)
print(lista)


[1, 4, 7]
[1, 4, 7, 9]

Función range()

Para hacer listas largas sin escribir los elementos uno por uno existen algunas funciones. Una de ellas es range(), que nos devuelve un objeto que mediante la función list() podemos convertirlo a una lista. Veamos cómo funciona mediante un ejemplo:


In [18]:
obj_range = range(10)
lista_range = list(obj_range)

print(obj_range, type(obj_range))
print(lista_range, type(lista_range))


range(0, 10) <class 'range'>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <class 'list'>

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.

Documentación de una función

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)))


[10, 11, 12, 13, 14]
[0, 2, 4, 6, 8]

Condicionales y booleans

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)


True

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 b
  • a<b Verifica si a es menor que b
  • a>=b Verifica si a es mayor o igual que b
  • a<=b Verifica si a es menor o igual que b
  • a==b Verifica si a es igual que b
  • a!=b Verifica si a es distinto que b

Tener en cuenta que para verificar la igualdad se usa == y no = ya que este último está reservado para asignar un valor.

Ejemplo


In [21]:
print(5 > 4)
print(5 < 4)
print(5 == 4)
print(5 != 4)
print(type(4 > 5))


True
False
False
True
<class 'bool'>

Condicionales (if, elif y else)

Veamos los condicionales con un ejemplo sencillo. Supongamos que queremos saber si un número es positivo, y si lo es, queremos printearlo.

Para poder poner este tipo de condiciones se usa el if. En caso:


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)


Este número es positivo:
4

Vemos que la estructura de cada uno de losif es algo así:

esto se ejecuta  o 
if BOOL:
    |esto se ejecuta
    |si BOOL es True

esto se ejecuta  o 

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)


Este número no es positivo
-3

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')


El número es negativo
-3

Por lo que la estructura completa queda:

esto se ejecuta  o 

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  o 

Ejercicio 2

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


For loops

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)


1.5
6.5
8.5
9.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)


0
1
2

De este modo podemos reproducir el resultado anterior usando el range.


In [27]:
for i in range(len(lista)):
    print(lista[i] + 0.5)


1.5
6.5
8.5
9.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]}')


El elemento 0 de las lista es 1
El elemento 1 de las lista es 6
El elemento 2 de las lista es 8
El elemento 3 de las lista es 9

Ejercicio 3

Realizar el Ejercicio 2 para todos lo números enteros positivos hasta el 10 utilizando un ciclo for.

Ayuda: Se pueden unir los ciclos for e if. Osea, esto vale:

for ALGUNA_ITERACIÓN:
    |if BOOL:
    |    ...
    |else:
    |    ...

y esto también:

if BOOL:
    |for ALGUNA_ITERACIÓN:
    |    ...

Funciones

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))


4.0
5.0
3.5

Aclaración

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.

Lambda functions

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.

La importancia de las referencias

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

Recursos

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

Y para seguir manijeando

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.

Agradecimientos

Todo esto es posible gracias al aporte de mucha gente.

  • Gente muy copada del DF como Hernán Grecco, Guillermo Frank, Osvaldo Santillán , Martín Elías Costa y Agustín Corbat por hacer aportes a estos talleres de diferentes maneras, desde poner su apellido para que nos presten un labo hasta venir como invitado a un taller.
  • El Departamento de Computación que cuatrimestre a cuatrimestre nos presta los labos desinteresadamente.
  • Los estudiantes del CODEP de Física, el CECEN y mucha gente que ayuda con la difusión.
  • Pibes de la FIFA que prestan su tiempo a organizar el material y llevan a cabo el taller.
  • Todos los que se acercan y piden que estos talleres se sigan dando y nos siguen llenando los Labos. Sí ¡Gracias a todos ustedes!