Lectura 6

Conceptos básicos de Python

  • Hoy aprenderemos un nuevo lenguaje de programación llamado Python, y recordemos que uno de los objetivos generales del curso no es aprender ningún lenguaje en particular, sino a programar en general.
  • El código fuente en Python se ve mucho más simple que en C, pero es capaz de resolver problemas en campos como la ciencia de datos. De hecho, para imprimir "hola, mundo", todo lo que necesitamos escribir es:

      print("hola, mundo")
    
    • Observe que, a diferencia de en C, no necesitamos importar una biblioteca estándar, declarar una función main, especificar un salto de línea en la función print o utilizar punto y coma.
  • Python es un lenguaje interpretado, lo que significa que en realidad ejecutamos otro programa (un intérprete) que lee nuestro código fuente y lo ejecuta de arriba hacia abajo. Por ejemplo, podemos guardar lo anterior como hello.py y ejecutar el comando python hello.py para ejecutar nuestro código, sin tener que compilarlo.

  • Podemos obtener cadenas de un usuario:

      answer = get_string("¿Cómo te llamas?\n")
      print("hola, " + answer)
    
    • Creamos una variable llamada answer, sin especificar el tipo (el intérprete lo determina por contexto para nosotros), y podemos combinar fácilmente dos cadenas con el operador + antes de pasarlo a print.
    • También podemos pasar varios argumentos a print, con print("hola,", answer), y también los unirá automáticamente con espacios.
    • print también acepta cadenas de formato como f"hola, {answer}", que sustituye las variables entre llaves en una cadena.
  • Podemos crear variables con solo counter = 0. Para incrementar una variable, podemos usar counter = counter + 1 o counter += 1.

  • Las condiciones se ven así:

      if x < y:
          print("x es menor que y")
      elif x > y:
          print("x es mayor que y")
      else:
          print("x es igual a y")
    
    • A diferencia de en C y JavaScript (en el que se utilizan llaves { } para indicar bloques de código), la sangría exacta de cada línea es lo que determina el nivel de anidación en Python.
    • Y en lugar de else if, solo decimos elif.
  • Las expresiones booleanas también son ligeramente diferentes:

      while True:
          print("hola, mundo")
    
  • Podemos escribir un bucle con una variable:

      i = 3
      while i > 0:
          print("tos")
          i -= 1
    
  • También podemos usar un bucle for, donde podemos hacer algo para cada elemento de una lista:

      for i in [0, 1, 2]:
          print("tos")
    
    • Las listas en Python son como matrices en C, pero pueden crecer y reducirse fácilmente con el intérprete administrando la implementación y la memoria por nosotros.
    • Este bucle for establecerá la variable i en el primer elemento, 0, se ejecutará, luego en el segundo elemento, 1, se ejecutará, y así sucesivamente.
    • Y podemos usar una función especial, range, para obtener algún número de valores, como en for i in range(3). Esto nos dará 0, 1 y 2, para un total de tres valores.
  • En Python, hay muchos tipos de datos:

    • bool, True o False
    • float, números reales
    • int, enteros
    • str, cadenas
    • range, secuencia de números
    • list, secuencia de valores mutables, que podemos cambiar, agregar o eliminar
    • tuple, secuencia de valores inmutables, que no podemos cambiar
    • dict, colección de pares clave/valor, como una tabla hash
    • set, colección de valores únicos
  • docs.python.org es la fuente oficial de documentación, pero Google y StackOverflow también tendrán recursos útiles cuando necesitemos descubrir cómo hacer algo en Python. De hecho, los programadores en el mundo real rara vez conocen todo en la documentación, sino más bien cómo encontrar lo que necesitan cuando lo necesitan.

Ejemplos

  • Podemos difuminar una imagen con:

      from PIL import Image, ImageFilter
    
      before = Image.open("bridge.bmp")
      after = before.filter(ImageFilter.BLUR)
      after.save("out.bmp")
    
    • En Python, incluímos otras librerías con import, y aquí importaremos los nombres Image y ImageFilter desde la librería PIL.
    • Resulta que, si buscamos documentación para la librería PIL, podemos usar las siguientes tres líneas de código para abrir una imagen llamada bridge.bmp, ejecutar un filtro de difuminado en ella y guardarla en un archivo llamado out.bmp.
    • Y podemos ejecutar esto con python blur.py luego de guardar en un archivo llamado blur.py.
  • Podemos implementar un diccionario con:

      words = set()
    
      def check(word):
          if word.lower() in words:
              return True
          else:
              return False
    
      def load(dictionary):
          file = open(dictionary, "r")
          for line in file:
              words.add(line.rstrip("\n"))
          file.close()
          return True
    
      def size():
          return len(words)
    
      def unload():
          return True
    
    • Primero, creamos un nuevo conjunto llamado words. Luego, para check, podemos simplemente preguntar if word.lower() in words. Para load, abrimos el archivo y usamos words.add para añadir cada linea a nuestro conjunto. Para size, podemos usar len para contar el número de elementos en nuestro conjunto, y finalmente, ¡para unload, no tenemos que hacer nada!
  • Resulta que, a pesar de que implementar un programa en Python es más simple para nosotros, el tiempo de ejecución de nuestro programa en Python es más lento que nuestro programa en C ya que nuestro intérprete tiene que hacer más trabajo por nosotros. Entonces, dependiendo de nuestros objetivos, también tendremos que considerar el compromiso del tiempo que nos lleva a los humanos escribir un programa que es más eficiente, en comparación con el tiempo de ejecución del programa.

  • En Python, también podemos incluir la biblioteca CS50, con una sintaxis específica:

      from cs50 import get_string
    
    • Ten en cuenta que especificamos las funciones que queremos utilizar.
  • Ahora podemos obtener cadenas de un usuario:

      from cs50 import get_string
    
      s = get_string("¿Cuál es tu nombre?:\n")
      print("hola, " + s)
    
  • También podemos sustituir expresiones en nuestras cadenas de formato:

      from cs50 import get_int
    
      age = get_int("¿Cuál es tu edad?\n")
      print(f"Tienes al menos {age * 365} días de vida.")
    
  • Y podemos demostrar condiciones:

      from cs50 import get_int
    
      x = get_int("x: ")
      y = get_int("y: ")
    
      if x < y:
          print("x es menor que y")
      elif x > y:
          print("x es mayor que y")
      else:
          print("x es igual a y")
    
  • Para comprobar condiciones, podemos decir:

      from cs50 import get_string
    
      s = get_string("¿Estás de acuerdo?\n")
    
      if s == "Y" o s == "y":
          print("De acuerdo.")
      elif s == "N" o s == "n":
          print("No de acuerdo.")
    
    • Python no tiene caracteres, por lo que podemos comprobarlos directamente como cadenas.
    • También podemos decir if s in ["Y", "y"]: o if s.lower() in ["y"]:. Resulta que las cadenas en Python son como estructuras en C, que no solo tienen variables sino también funciones que se pueden llamar. Por ejemplo, dada una cadena s, podemos llamar a su función lower con s.lower() para obtener la versión en minúsculas de la cadena.

Podemos mejorar la versión de tos también:

    print("tos")
    print("tos")
    print("tos")

- No necesitamos declarar una función `main`, por lo que tan solo escribimos la misma línea de código tres veces.
  • Pero podemos hacerlo mejor:

      for i in range(3):
          cough()
    
      def cough():
          print("tos")
    
    • Tenga en cuenta que no necesitamos especificar el tipo de retorno de una nueva función, que podemos definir con def.
    • Pero esto provoca un error cuando tratamos de ejecutarlo: NameError: no se ha definido el nombre 'tos'. Resulta que necesitamos definir nuestra función antes de usarla, por lo que podemos mover nuestra definición de tos al inicio o crear una función main:

        def main():
            for i in range(3):
                cough()
      
        def cough():
            print("tos")
      
        main()
      
    • Ahora, en el momento en que llamamos nuestra función main, la función de tos habrá sido leída por nuestro intérprete.

  • Nuestras funciones también pueden tomar entradas:

``` def main(): cough(3)

def cough(n): for i in range(n): print("cough")

main() ```

  • Podemos definir una función para obtener un número entero positivo:

``` from cs50 import get_int

def main(): i = get_positive_int() print(i)

def get_positive_int(): while True: n = get_int("Entero positivo: ") if n > 0: break return n

main() ```

  • Como en Python no hay un bucle do-while como en C, hay un bucle while que continuará infinitamente, sin embargo, utilizamos break para finalizar el bucle tan pronto como n > 0. Después, nuestra función simplemente devuelve n.
  • Observa que las variables en Python tienen un ámbito de función por defecto, lo que quiere decir que n se puede inicializar dentro de un bucle y aun seguir siendo accesible más adelante en la función.
  • Podemos imprimir una fila de signos de interrogación en la pantalla:

for i in range(4): print("?", end="") print()

  • Cuando imprimimos cada bloque, no queremos un salto de línea automático, así que podemos pasar un parámetro, o argumento con nombre, a la función print. Aquí decimos end="" para especificar que no se debe imprimir nada al final de nuestra cadena. Luego, después de imprimir nuestra fila, podemos llamar a print para obtener un salto de línea.

  • También podemos "multiplicar" una cadena e imprimirla directamente con: print("?" * 4).

  • Podemos imprimir una columna con un bucle:

      for i in range(3):
          print("#")
    
  • Y sin un bucle: print("#\n" * 3, end="").

  • Podemos implementar bucles anidados:

      for i in range(3):
          for j in range(3):
              print("#", end="")
          print()
    
  • No necesitamos usar la función get_string de la biblioteca CS50, ya que podemos usar la función input incorporada en Python para obtener una cadena del usuario. Pero si queremos otro tipo de datos, como un entero, del usuario, tendremos que convertirlo con int().

  • Pero nuestro programa fallará si la cadena no se puede convertir a un entero, por lo que podemos usar get_string que simplemente preguntará de nuevo.
  • En Python, intentar obtener un desbordamiento de enteros en realidad no funcionará:

      from time import sleep
    
      i = 1
      while True:
          print(i)
          sleep(1)
          i *= 2
    
    • Llamamos a la función sleep para pausar nuestro programa durante un segundo entre cada iteración.
    • Esto continuará hasta que el entero ya no pueda caber en la memoria de su computadora.
  • También se puede prevenir la imprecisión de punto flotante mediante bibliotecas que pueden representar números decimales con tantos bits como sean necesarios.

  • Podemos hacer una lista:

      scores = []
      scores.append(72)
      scores.append(73)
      scores.append(33)
    
      print(f"Average: {sum(scores) / len(scores)}")
    
    • Con append, podemos agregar elementos a nuestra lista, usándola como una lista enlazada.
    • También podemos declarar una lista con algunos valores como scores = [72, 73, 33].
  • Podemos iterar sobre cada carácter en una cadena:

      from cs50 import get_string
    
      s = get_string("Entrada:  ")
      print("Salida: ", end="")
      for c in s:
          print(c, end="")
      print()
    
    • Python obtendrá cada carácter de la cadena por nosotros.
  • Para poner una cadena en mayúsculas también, podemos simplemente llamar a s.upper() para obtener la versión en mayúsculas de toda la cadena, sin tener que iterar sobre cada carácter nosotros mismos.

Más funcionalidades

  • Podemos tomar argumentos de línea de comandos con:

      from sys import argv
    
      for i in range(len(argv)):
          print(argv[i])
    
    • Dado que argv es una lista de strings, podemos utilizar len() para obtener su longitud y range() para un rango de valores que podemos utilizar como índice para cada elemento en la lista.
  • Pero también podemos dejar que Python itere sobre la lista por nosotros:

      from sys import argv
    
      for arg in argv:
          print(arg)
    
  • También podemos devolver códigos de salida cuando nuestro programa cierra:

      from sys import argv, exit
    
      if len(argv) != 2:
          print("Falta argumento de línea de comandos")
          exit(1)
      print(f"Hola, {argv[1]}")
      exit(0)
    
    • Importamos la función exit y la llamamos con el código con el que queremos que nuestro programa cierre.
  • Podemos implementar la búsqueda lineal simplemente comprobando cada elemento de una lista:

      import sys
    
      nombres = ["EMMA", "RODRIGO", "BRIAN", "DAVID"]
    
      if "EMMA" in nombres:
          print("Encontrado")
          sys.exit(0)
      print("No encontrado")
      sys.exit(1)
    
  • Si tenemos un diccionario, un conjunto de pares clave:valor, también podemos verificar cada clave:

      import sys
    
      personas = {
          "EMMA": "617-555-0100",
          "RODRIGO": "617-555-0101",
          "BRIAN": "617-555-0102",
          "DAVID": "617-555-0103"
      }
    
      if "EMMA" in people:
          print(f"Encontrado {people['EMMA']}")
          sys.exit(0)
      print("No encontrado")
      sys.exit(1)
    
    • Observe que podemos obtener el valor de una clave particular en un diccionario con people['EMMA']. Aquí, usamos comillas simples (se permiten tanto comillas simples como dobles, siempre y cuando coincidan con una cadena) para diferenciar la cadena interna de la externa.
    • Y declaramos diccionarios con llaves, {}, y listas con corchetes [].
  • En Python, podemos comparar cadenas directamente con solo ==:

      from cs50 import get_string
    
      s = get_string("s: ")
      t = get_string("t: ")
    
      if s == t:
          print("Igual")
      else:
          print("Diferente")
    
  • Copiar cadenas también funciona sin ningún trabajo adicional de nuestra parte:

      from cs50 import get_string
    
      s = get_string("s: ")
    
      t = s
    
      t = t.capitalize()
    
      print(f"s: {s}")
      print(f"t: {t}")
    
  • También se puede intercambiar dos variables asignando ambos valores al mismo tiempo:

      x = 1
      y = 2
    
      print(f"x es {x}, y es {y}")
      x, y = y, x
      print(f"x es {x}, y es {y}")
    

Archivos

  • Vamos abrir un archivo CSV:

      import csv
      from cs50 import get_string
    
      archivo = open("agenda.csv", "a")
    
      nombre = get_string("Nombre: ")
      numero = get_string("Número: ")
    
      escritor = csv.writer(archivo)
      escritor.writerow((nombre, numero))
    
      archivo.close()
    
    • Resulta que Python también tiene un paquete (biblioteca) csv que nos permite trabajar con archivos CSV, por lo que después de abrir el archivo para añadir información, podemos llamar a csv.writer para crear un escritor a partir del archivo y luego writer.writerow para escribir una fila. Con los paréntesis internos, estamos creando una tupla con los valores que queremos escribir, por lo que en realidad estamos pasando un solo argumento que tiene todos los valores para nuestra fila.
  • Podemos utilizar la palabra clave with, que nos ayuda a cerrar el archivo:

      ...
      with open("agenda.csv", "a") as archivo:
          escritor = csv.writer(archivo)
          escritor.writerow((nombre, numero))
    

Nuevas funciones

  • Una característica de Python que C no tiene son las expresiones regulares, o patrones con los que podemos emparejar cadenas. Por ejemplo, su sintaxis incluye:
    • ., para cualquier carácter
    • .*, para 0 o más caracteres
    • .+, para 1 o más caracteres
    • ?, para algo opcional
    • ^, para el inicio de la entrada
    • $, para el final de la entrada
  • Por ejemplo, podemos emparejar cadenas con:

      import re
      from cs50 import get_string
    
      s = get_string("¿Estás de acuerdo?\n")
    
      if re.search("^y(es)?$", s, re.IGNORECASE):
          print("Acordado.")
      elif re.search("^no?$", s, re.IGNORECASE):
          print("No acordado.")
    
    • Primero, necesitamos el paquete, o biblioteca, re para expresiones regulares.
    • Luego, para y o yes, tenemos la expresión regular ^y(es)?$. Queremos asegurarnos de que la cadena comience con y, y opcionalmente tenga es inmediatamente después de y, y luego termine.
    • De manera similar, para n y no, queremos que nuestra cadena comience, tenga la letra n, y opcionalmente la letra o después, y luego termine. La expresión regular para eso sería ^no?$.
    • Pasamos otro argumento, re.IGNORECASE, para ignorar las mayúsculas y minúsculas de las letras en la cadena.
    • Si ninguna de las expresiones regulares coincide, no imprimimos nada.
  • En nuestra propia Mac o PC, podemos abrir una terminal después de instalar Python y utilizar el micrófono para convertir nuestro discurso en texto:

      import speech_recognition
    
      recognizer = speech_recognition.Recognizer()
      with speech_recognition.Microphone() as source:
          print("¡Di algo!")
          audio = recognizer.listen(source)
    
      print("Google Speech Recognition cree que has dicho:")
      print(recognizer.recognize_google(audio))
    
    • Resulta que hay otra biblioteca que podemos descargar, llamada speech_recognition, que puede escuchar audio y convertirlo en una cadena.
  • Y ahora, podemos hacer coincidir el audio para imprimir otra cosa:

      ...
      words = recognizer.recognize_google(audio)
    
      # Responder al discurso
      if "hello" in words:
          print("¡Hola a ti también!")
      elif "how are you" in words:
          print("¡Estoy bien, gracias!")
      elif "goodbye" in words:
          print("¡Adiós a ti también!")
      else:
          print("¿Qué?")
    
  • Incluso podemos utilizar expresiones regulares para hacer coincidir parte de una cadena:

      ...
      words = recognizer.recognize_google(audio)
    
      matches = re.search("my name is (.*)", words)
      if matches:
          print(f"Hola, {matches[1]}.")
      else:
          print("Hola, tú.")
    
    • Aquí, podemos obtener todos los caracteres después de my name is con .* e imprimirlos.
  • Ejecutamos detect.py y faces.py, que encuentra cada rostro (o incluso un rostro específico) en una foto.

  • qr.py también generará un código QR en una URL en particular.