INTRODUCCIÓN A LA PROGRAMACIÓN CON PYTHON I

Módulo 1 - Introducción

Variables

Asignando variables

Una variable es como cuando aprendimos en álgebra que equis «x» es igual a un número desconocido. La diferencia es que aquí conocemos bien ese valor, pero no necesariamente es un número. Puede ser texto, números, booleanos, u otras estructuras de datos más complejas.

x = 3

x <- variable

3 <- valor

x
3
x = 'manzana'   # misma variable pero ahora tiene un valor diferente, que además es texto
print(x)
manzana
x               # Nótese que volvimos a imprimir el valor de x, pero ahora tiene comillas simples.
                # Más adelante veremos estos detalles del texto
'manzana'
x = 4           # misma variable, nuevo valor
y = 1           # "y" también es una variable, pero con diferente valor!
x + y           # dependiendo del "tipo" de variable, podemos hacer ciertas operaciones con ellas
5
print(x)        # nótese como podemos "imprimir" el valor de la variable 
print(y)        
4
1
x = y           # asignando el valor de y a la variable x
x
1
y
1
z = True         # Verdadero y Falso son los únicos dos valores booleanos
z = False
z                # Nótese que en Python, siempre van a ir en mayúscula el valor booleano
False

¿Cómo nombrar variables?

## Buenos nombres de variables
segundos = 60
horas = 24
dia = 30

## no tan buenos
x = 60
y = 24
z = 30
# mucho más fácil entender qué es lo que calculamos aquí
print(segundos*horas*dia)

# que lo que estamos calculando acá
print(x*y*z)
43200
43200
e90 = 1
fruta = 'manzana'
fruta
'manzana'
fruta_roja = 'manzana'
fruta_rojá  = 'manzana'
fruta_rojá  = 'manzaná'

Tipos de datos

Existen varios tipos «fundamentales» de datos:

  • strings, texto

  • ints/integers, números enteros (-4, 0, 27)

  • floats/doubles, números reales (también llamados de punto flotante, pero nunca les llamaremos así en el curso; 4.0, 2.000000001)

  • bool/booleans, booleanos

  • None, un tipo particular de dato nativo a Python

Hay otros tipos de datos pero por el momento sólo hablaremos de estos.

Strings (o sea, texto)

El texto en Python es muy fácil de manipular. Generalmente podemos escribir acentos y cualquier tipo de caracter, pero hay que tener cuidado a la hora de leer y escribir archivos.

Para que un texto sea tal, necesita estar entre comillas simples ' o dobles ".

x = "¡Hola mundo!"
x
'¡Hola mundo!'
## el texto nos permite hacer ciertas operaciones como la concatenación
x = "Hola"
y = "mundo"

x + y     # nótese que no pusimos ningún espacio, el texto se concatenó exactamente como le dijimos
'Holamundo'
x + " " + y     # ahora sí pusimos un espacio con " "
'Hola mundo'
x[0]   # el texto nos cae bien porque podemos extraer pedazos de él. 
       # Esto se llama indexar y lo cubriremos más adelante.
       # Nótese como Python es un lenguaje que empieza a contar desde el cero
'H'
x[1]   # más adelante explicaremos por qué esta sintaxis, pero es una forma de acceder al i-ésimo elemento
'o'
x[1:5] # similar a arriba, pero ahora queremos del 2o al 4o elemento. 
       # Nótese que no es hasta el 5o elemento, más adelante veremos a detalle por qué sucede así
'ola'
x[5:]
''
x[-1]    # ohh, el último valor!
'a'
x[-2]   # Todas estas son formas de acceder al valor en una posición del texto. Cuando lleguemos a las listas,
        # entenderemos mejor cómo acceder a un valor con este tipo de sintaxis
'l'
x[-5:-1]
'Hol'
x[0], x[-1]
('H', 'a')
3*x  ## la multiplicación en este caso es concatenación repetida x veces
'HolaHolaHola'
x[:]
'Hola'
x = hola   ## hola a secas no es texto, es una variable - no olvidemos las comillas para definir texto!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-36-9844f3645071> in <module>
----> 1 x = hola   ## hola a secas no es texto, es una variable - no olvidemos las comillas para definir texto!

NameError: name 'hola' is not defined
## las comillas triples nos ayudan a hacer textos multilinea
x = """
hola mundo
como estas
tengo hambre
"""
print(x)
hola mundo
como estas
tengo hambre
x = "hola mundo"
print(x)
hola mundo
x           ## ¿Por qué aquí salen comillas y con print() no?
'hola mundo'
x = "El profesor murmuró: "
y = 'Hola mundo'
print(x, y)  ## print nos permite imprimir más de una cosa en pantalla
El profesor murmuró:  Hola mundo
print(x)print(y)
  File "<ipython-input-44-2ff1f06abb52>", line 1
    print(x)print(y)
                ^
SyntaxError: invalid syntax

Ints/Integers (enteros)

Si recuerdan sus clases de precálculo o cálculo, recordarán que los enteros son números no fraccionales, positivos, negaticos, y el cero. En otras palabras:

\[z\in \mathbb{Z}, z\in \{...,-3, -2, -1, 0, 1, 2, 3,...\}\]
x = -2
(x + 3)*2 + 1
3

Ok, breve recordatorio de aritmética y álgebra. ¿Cuál es el orden de operaciones en Python? 1. Operaciones dentro de paréntesis () 2. Logaritmos y exponenciales 3. Multiplicación y división 4. Suma y resta

En la operación de arriba, el orden de ejecución es: 1. (-2 + 3) 2. (1)*2 3. 2 + 1

# ¿Cuál es el valor de y aquí abajo?
x = 2
y = x + 3*2 + 1
y
9
# ¿Qué tal ahora?
y = x + 3*(2 + 1)
y
11
# Cualquier número por cero es igual a cero
x = 2
x*0
0
# El exponencial se puede escribir de más de una manera en Python, pero el doble asterisco
# es la más fácil y la que cubriremos por ahora:
x**5
32
# Recordemos el orden de ejecución de operaciones. Primero exponenciales y hasta el final sumas
x**2+1
5
# Una exponencial fraccionada es una raíz i-ésima. En este caso, es equivalente a sacar la raíz cuadrada
# nótese que el valor de esta operación ya no es un entero, sino otro tipo de valor
x**(1/2)
1.4142135623730951
# ¿Recuerdan la división entre cero?
x/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-55-57dda22cbbbf> in <module>()
      1 # ¿Recuerdan la división entre cero?
----> 2 x/0

ZeroDivisionError: division by zero
x = manzana
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-56-d3900484e66d> in <module>()
----> 1 x = manzana

NameError: name 'manzana' is not defined

Como notarán, el error dice explícitamente «división entre cero». Hay varios tipos de error en Python pero una de las cosas más importantes es aprender a leer y entender los errores. Si no tuviéramos un error explícito que nos dijera que estamos dividiendo entre cero, talvez nos tardaríamos mucho tiempo en encontrar el error de lo que estamos ejecutando.

## ¿Qué pasará si intentamos hacerle como con el texto, para extraer un valor de un entero?
x = 25
x[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-57-ca0cdccf12bc> in <module>()
      1 ## ¿Qué pasará si intentamos hacerle como con el texto, para extraer un valor de un entero?
      2 x = 25
----> 3 x[0]

TypeError: 'int' object is not subscriptable

No todas las operaciones son válidas para todos los tipos de variables. Cada tipo de dato (enteros, strings, etc.) tiene diferentes operaciones (propiamente llamados «métodos»). Iremos viendo cómo usar cada una de esas operaciones y para qué sirven.

Floats/Doubles (números reales)

Todos los números reales no imaginarios. En otras palabras:

\[x\in \mathbb{R}, x\in (-\infty, \infty)\]

1 es real. 2.55 es real. \(\pi\) \((3.141592654...)\) es un número real. \(\sqrt2\) es real. \(1.1111...\) es un número real. \(10^{100}\) es real.

Los números enteros los podemos representar con un número real. Sin embargo, hay diferencias detrás de cámaras que hacen esos dos tipos de datos muy diferentes. Las más importantes para nosotros en este punto son:

  • Los floats ocupan más memoria que los enteros, así que tardaremos un poco más en hacer operaciones

  • La aritmética con floats será frecuentemente inexacta debido a la forma en la que los datos se guardan en la memoria

y = 3.2
y*2
6.4
y*y      # ¿Qué es ese 0000000002 que no debería estar ahí?
         # justo a esto nos referimos con que la aritmética será inexacta con los floats
10.240000000000002
z = (y*10)
z
32.0
z*z       # Por qué ahora no tuvimos ese 00000002 ahí?
1024.0
(z*z)/(10*10)
10.24
y / 2    # la diagonal hace una división normal 
1.6
y // 2   # doble diagonal es el cociente de una división normal 
# en este caso, el cociente de 3.2 entre 2 es 1 
1.0
y % 2    # esta operación es el residuo o "módulo" 
# en este caso, el cociente de 3.2 entre 2 es 1 y el residuo es 1.2
# nótese que como hicimos operaciones con floats, tenemos otra vez el tema
# de un 000000002 al final del número
1.2000000000000002
x = 1.0
y = 1
x, y       # nótese que para Python, un número decimal automáticamente define un float vs un entero
(1.0, 1)
# float con entero hace float
# entero con entero hace entero
3*x, 3*y   
(3.0, 3)
# float con entero hace float
# entero con entero hace entero
x + 1, y + 1
(2.0, 2)
# float con entero hace float
y + x
2.0
# float con entero hace float
x - y
0.0
import math
math.log(10)

## omg qué es esto? *import* es un comando para importar una biblioteca
## una biblioteca es una colección de código que nos ayuda a hacer cosas,
## en este caso, a hacer operaciones matemáticas. Más adelante introduciremos
## otro tipo de bibliotecas.
2.302585092994046
round(math.log(10), 4)
2.3026

Booleanos

# Como mencionamos arriba, los booleanos son sólo estos dos valores:
x = True
y = False
# Los booleanos tienen que ser con la primera letra mayúscula, si no, no se identifican como booleanos
x = true
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-78-eb3ab214f101> in <module>()
      1 # Los booleanos tienen que ser con la primera letra mayúscula, si no, no se identifican como booleanos
----> 2 x = true

NameError: name 'true' is not defined
print(x)
True

Recordatorio de lógica:

  • La función y (and) lógica se evalúa de la siguiente manera:

    • True and True = True

    • True and False = False

    • False and False = False

    • La función y requiere que todo sea verdadero para obtener verdadero. Un solo falso hará que la función se evalúe como falsa.

  • La función o (or) lógica se evalúa de la siguiente manera:

    • True or True = True

    • True or False = True

    • False or False = False

    • La función o requiere que al menos un elemento sea verdadero para obtener verdadero. Si todo es falso, entonces obtendremos un falso”

x and x
True
x and y
False
y and y
False
x or y
True
y or y
False

Las comparaciones numéricas también regresan valores lógicos. Las siguientes operaciones se admiten para hacer comparaciones:

  • x > y, x es mayor que y

  • x < y, x es menos que y

  • x >= y, x es mayor o igual que y

  • x <= y, x es menor o igual que y

  • x == y, x es igual que y

3 > 1
True
3 >= 1
True
3 < 1
False

Cuando comparamos valores booleanos, en vez de usar el ==, usamos el operador is:

x = True
x is (3 > 1)
True
x is (3 < 1)
False
z = 1
z == 1
True
z == 2
False
x is 1
False
x + 1    # ¡¿Pero qué está pasando aquí?!
2

Python interpreta el valor True como 1 en algunas circunstancias. Esto es algo no necesariamente deseable ya que puede tener efectos como lo anterior. Esta es una razón importante para no mezclar tipos de datos.

x is 1
False
1 == True
True
x == 1
True
2*x
2
y = False
2*y
0

Igual que con True, Python interpreta False como un cero.

y == 0
True
y == 0
True

Recap: Hasta ahora hemos visto 4 tipos de datos «fundamentales»: strings, enteros, floats y booleanos.

x = True
type(x)
bool
x = 1
type(x)
int
x = 1.0
type(x)
float
x = '1'
type(x)
str

None

Hay un tipo de dato extra en Python que nos ayuda a definir un valor «nulo»: el tipo None. Este valor es útil cuando queremos definir una variable pero todavía no le queremos asignar ningún valor. Más adelante veremos en qué casos puede sernos útil.

x = None
x is False
False
x is True
False
x == 1
False
x == 0
False
x is None
True
x + 1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-111-eaf7b6991020> in <module>()
----> 1 x + 1

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

None acepta ciertas comparaciones pero no operaciones aritméticas. Como vemos, ese último error nos dice que no podemos sumar None con int.

Expresiones

Una expresión es código que al ejecutarse, se evalúa. Por ejemplo, x=1 no se evalúa, se declara («x es igual al valor 1»). ¿A qué nos referimos con evaluar? A que Python debe hacer alguna operación para llegar a un resultado. A continuación ejemplos:

# Suma/resta, multiplicación/división son ejemplos básicos de expresiones
- 3 + 5*(120/12) 
47.0
# Concatenación de strings
3 * 'Hola Mundo!'
'Hola Mundo!Hola Mundo!Hola Mundo!'
nombre = "Xóchitl"                # esto es una declaración, asignamos un valor a una variable
'Hola, me llamo: ' + nombre       # esto es una expresión, porque evaluamos una operación que es concatenación
'Hola, me llamo: Xóchitl'
total = 57.25
"Su total es de $" + str(total)   ## casting! Esta función nos ayuda a convertir variables de un tipo a otro
'Su total es de $57.25'
# una comparación lógica es una evaluación ya que Python intenta decirte si algo es verdadero o no
25 > 5*5
False
25 >= 5*5
True
edad = 25 
edad != 12*2+1
False
# comparación con strings -> orden alfabético basado en lenguaje default en Python (unicode)
"A" > "B"
False
"A" < "B"
True
"A" < "a"
True
## comparando tipos distintos
1 > '1'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-123-1e1227b8f48c> in <module>()
      1 ## comparando tipos distintos
----> 2 1 > '1'

TypeError: '>' not supported between instances of 'int' and 'str'
x = '1'
1 == int(x)   # casting de nuevo, para una comparación lógica

# Nota que == es diferente a =
# La sintaxis == es un comparador lógico
# El signo de igual = es sólo para asignar variables
True
x = 25
25.0 == x
True
# recordemos que algunas comparaciones con floats no funcionarán por los problemas de 
# aritmética que suceden con dichos datos 
x = 3.2
x*x == 10.24
False
x*x
10.240000000000002
x = 2.1
x*x == 4.41
True
x*x
4.41
## Ejemplo - debo pagar impuestos? 
## Si eres persona física y ganas más del ingreso mínimo, sí.

# Inputs
salario = 12500
ingreso_min = 10000
persona = 'física'

# Evaluar si debo pagar impuestos
(persona == 'física') and (salario > ingreso_min)
True
## Debo pagar impuestos? 
## Si eres persona física y tus ingresos menos deducciones son más del ingreso mínimo, sí.

# Inputs
salario = 12500
deduccion = 5000
ingreso_min = 10000
persona = 'física'

# Evaluar si debo pagar impuestos
(persona == 'física') and (salario - deduccion > ingreso_min)
False

Ejercicios

¿Cuál es el valor de w?

x = 12
y = -1
z = 0

w = x*2 - y + 1**z
w
26

¿Cuál es el valor de la variable c?

a = 'Hola'
b = 'Mundo'
c = a[:2] + '__' + b[1:]
c
'Ho__undo'
a[1:]
'ola'

¿Cuál es el valor de la variable d?

a = 1
b = 2.0
c = '7'

d = str((3*a + b/0.5) / int(c))
d
'1.0'
type(str(7))
str

Supón que eres un inversionista que quiere comprar Bitcoin (BTC) en México pero sólo tiene dólares (USD) en EUA. Para comprar BTC en México, primero debe convertir sus USD a pesos mexicanos (MXN) y luego comprar BTC. El banco cobra 1% para convertir USD a MXN. Comprar BTC tiene un costo del 0.25% de la transacción.

Escribe abajo código que te ayude a calcular cuántos USD debes transferir a tu cuenta en México para comprar 0.1 BTC. Usa variables para apoyarte en este cálculo.

# input
bitcoin_a_pesos = 216860.59
dolares_a_pesos = 21.69

bitcoin_objetivo = 0.1
bitcoin_buy_fee = 0.0025
usd_to_mxn_fee = 0.01

pesos_necesarios = bitcoin_objetivo * bitcoin_a_pesos *(1 + bitcoin_buy_fee)    # 21740.2741475
dolares_necesarios = pesos_necesarios * (1 + usd_to_mxn_fee)/dolares_a_pesos   # 1012.3410276152605
dolares_necesarios
1012.3410276152605

Declaraciones (statements)

Las declaraciones son las instrucciones para Python. Hay varios tipos de declaraciones:

  • Asignación (x=1)

  • Funcionales (print("Hola"))

  • Control de flujo (más info abajo)

Los comandos que hemos estado ejecutando hasta ahora también son declaraciones. Hemos asignado variables, ejecutado algunas funciones como print y evaluado expresiones como las lógicas; todos estos ejemplos son declaraciones.

La diferencia entre una expresión y una declaración es que la primera evalúa una operación/función, mientras que la declaración se refiere a más tipos de acciones.

No es necesario que sepamos la diferencia exacta entre una declaración y una expresión, pero en algunos libros encontrarán esta distinción y es importante saber a qué se refiere cada palabra.

Condicionales (if statements)

Ya hemos visto declaraciones de asginación y algunos ejemplos de funciones. Ahora nos enfocaremos en statements de flow control. El primer statementque veremos son los if, llamados condicionales.

ph = 7

if ph > 7:
    print('base')    # 4 espacios!

Los 4 espacios son sintaxis necesaria en Python al escribir varias declaraciones, entre ellas las condicionales . Sin estos 4 espacios, tendremos un error.

if ph > 7:
print('base')
  File "<ipython-input-139-cba2536d5d58>", line 2
    print('base')
        ^
IndentationError: expected an indented block

Nota que el error de este error de sintaxis es muy claro: Indentation Error. Hay que escribir 4 espacios siempre. Hay algunos editores de texto o interpretadores de código que en vez de usar 4 espacios usan el caracter de indentación. Recomendamos usar siempre 4 espacios.

Nota

Escribir código no es muy diferente a hacer una presentación en PPT, un paper académico, o un oficio gubernamental. Al final del día es lenguaje que debe ser entendido por una máquina, pero también por un ser humano que a veces no somos nosotros. Es muy importante que el código que escribimos siga cierta sintaxis (qué se escribe) y cierto estilo (cómo lo escribimos). Recomendamos seguir la guía de estilo Python Style Guide: simplified version for beginner programmers de John Magee: http://www.cs.bu.edu/courses/cs108/guides/style.html . Por ahora puede que no tenga tanto sentido, pero a medida que vayamos aprendiendo a escribir más código, muchas de estas reglas nos serán útiles.

ph = 7.5
if ph > 7:
    print('base') 
base
## ¿Qué hacemos si también quiero que me diga si es un ácido?
ph = 7

if ph > 7:
    print('base')
if ph < 7:
    print('ácido')
## ¡Oh no, pero el ph 7 no es ácido ni base, es neutro! ¿Cómo agregamos eso?
ph = 7

if ph > 7:
    print('base')
if ph < 7:
    print('ácido')
if ph == 7:
    print('neutro')
neutro

Yujú! Pero ahora, supongamos que es un experimento y cuando el ph es base, le reducimos a la solución el ph mágicamente en 1; análogamente, cuando el ph es ácido, le subimos el ph en 1. ¿Cómo afectaría eso el ph final?

ph = 7.2

if ph > 7:
    ph = ph - 1
    print(ph)
if ph < 7:
    ph = ph + 1
    print(ph)
    
if ph > 7:
    print('base')
if ph < 7:
    print('ácido')
if ph == 7:
    print('neutro')
6.2
7.2
base

¡Oh no!¿Pero qué está pasando?

ph = 7.2

if ph > 7:
    ph = ph - 1
    print("restando 1", ph)
if ph < 7:
    ph = ph + 1
    print("sumando 1", ph)

print(ph)
restando 1 6.2
sumando 1 7.2
7.2

La solución de estas situaciones, los comandos else y elif.

ph = 7.2

if ph > 7:
    ph = ph - 1
elif ph < 7:
    ph = ph + 1

print(ph)

# como en este momento la variable ph no cambia, no es necesario usar un elif o else
if ph > 7:
    print('base')
if ph < 7:
    print('ácido')
if ph == 7:
    print('neutro')
6.2
ácido

supongamos que ahora queremos imprimir un mensaje cuando el ph es exactamente 7, ¿cómo lo hacemos?

ph = 7.0

if ph > 7:
    ph = ph - 1
elif ph < 7:
    ph = ph + 1
else:
    print("¡Sin cambios!")

print(ph)

# como en este momento la variable ph no cambia, no es necesario usar un elif o else
if ph > 7:
    print('base')
if ph < 7:
    print('ácido')
if ph == 7:
    print('neutro')
¡Sin cambios!
7.0
neutro

Ejercicio

Una máquina tragamonedas tiene la siguiente fórmula para determinar si alguien gana premio:

  • La apuesta inicial siempre tiene que ser un número mayor a 0

  • La apuesta inicial se divide entre 15

  • Se toma la parte entera de la división y si es mayor a 0, se multiplica por (3 + el residuo); si es igual a 0, se le agrega 1.5 y se multiplica por el residuo del paso anterior

Ejemplo del paso anterior - si la apuesta era 2, dividir 2 entre 15 da un cociente entero de 0 y un residuo de 2. Entonces el resultado del paso anterior es 1.5 * 2 - si la apuesta era 34, dividir 34 entre 15 da un cociente entero de 2 y un residuo de 4. Entonces el resultado del paso anterior es 2 * (3+4)

  • Finalmente, si el resultado es divisible entre 7, el jugador gana.

Ejemplo, apuesta inicial: 24. Apuesta inicial entre 15: 1, residuo 9. 1 * (3 + 9) = 10 El residuo de dividir 10 / 7 no es igual a 0. El jugador no gana.

apuesta = 24

cociente = apuesta // 15
residuo = apuesta % 15

if cociente > 0:
    rv = cociente * (3 + residuo)
else: 
    rv = (cociente + 1.5)*residuo

if rv % 7 == 0:
    print("¡Ganador!")
else:
    print("Perdedor :(")
Perdedor :(

supongamos que hay un bug en la máquina de apuestas y ocasionalmente lee las apuestas como números negativos. Modifica tu código para que al leer la apuesta, lo cambie a un número positivo.