Laboratorio 1: Primeros pasos 👣

MDS7202: Laboratorio de Programación Científica para Ciencia de Datos - Primavera 2022

Cuerpo Docente:¶

  • Profesor: Matías Rojas y Mauricio Araneda
  • Auxiliar: Ignacio Meza D.
  • Ayudante: Rodrigo Guerra

Equipo: SUPER IMPORTANTE - notebooks sin nombre no serán revisados¶

  • Nombre de alumno 1: Johnny Godoy

Link de repositorio de GitHub: https://github.com/johnny-godoy/laboratorios-mds/tree/main/lab%201¶

Reglas:¶

  • Fecha de entrega: 6 días desde la publicación, 3 días de atraso con 1 punto de descuento c/u. Pueden utilizar días bonus sin descuento.
  • Grupos de 2 personas.
  • Ausentes deberán realizar la actividad solos.
  • Cualquier duda fuera del horario de clases al foro. Mensajes al equipo docente serán respondidos por este medio.
  • Prohibidas las copias.
  • Pueden usar cualquer material del curso que estimen conveniente.

Objetivos del lab:¶

  • Variables, Operadores y Expresiones.
  • Estructuras de Control (if/else).
  • Iteraciones.
  • Listas y Diccionarios.
  • Programación Funcional (Map, Filter y Reduce).
  • Programación Orientada a Objetos (Encapsulamiento, Polimorfismo y Herencia)

Parte 1: 🍋 Frutas 🍓¶

En esta primera parte se trabajará lo escencial visto hasta el momento más un poco de programación funcional a través de frutas:

Defina las siguientes frutas como diccionarios a partir de sus características (nombre, color, tipo y si posee o no pepas):

Nombre Tipo Color Pepas
limon cítrica amarillo True
naranja cítrica naranjo True
plátano tropical amarillo False
piña tropical amarillo False
frutilla bosque rojo True
frambuesa bosque rojo True

[0.5] Estructurar Datos¶

Agregue Piña, Frutilla y Frambuesa como diccionarios (siga los ejemplos de las frutas dadas) y luego cree un arreglo que contenga estas frutas.

In [1]:
limon = {
    "nombre": "limón", 
    "tipo": "cítrica", 
    "color": "amarillo", 
    "pepas": True
    }

naranja = {"nombre": "naranja", 
           "tipo": "cítrica", 
           "color": "naranjo", 
           "pepas": True
          }

platano = {"nombre": "plátano", 
           "tipo": "tropical", 
           "color": "amarillo", 
           "pepas": False
          }

# agregar las frutas que faltan aquí
piña = {"nombre": "piña",
        "tipo": "tropical",
        "color": "amarillo",
        "pepas": False
       }

frutilla = {"nombre": "frutilla",
            "tipo": "bosque",
            "color": "rojo",
            "pepas": True
           }

frambuesa = {"nombre": "frambuesa",
             "tipo": "bosque",
             "color": "rojo",
             "pepas": True
            }

Ahora, agregue todas las frutas a un arreglo.

In [2]:
# Ojo: agregue los datos en el mismo orden que aparecen en la tabla
datos = [limon, naranja, platano, piña, frutilla, frambuesa]

Este arreglo será la información con la que se trabajará durante el lab.

Funciones Aplicadas a los Datos [1.5 puntos]¶

Elabore las siguientes funciones:

1. La función describir(datos) que genere un arreglo con strings que describan cada fruta de la siguiente manera:

{El/La} {...} es una fruta de tipo {...} de color {...}. {Presenta/No presenta} pepas en su interior.

Por ejemplo, para el plátano se debe generar el siguiente string:

'El plátano es una fruta de tipo tropical de color amarillo. No presenta pepas en su interior.'

Indicaciones:

  • Usen formateo o suma de strings, if, else y for.
  • Para saber si ocupar 'El' o 'La', pueden acceder al nombre de la fruta y ver cual es el último carácter a través de indexadores string[indice].

2. La función filtrar_por_pepa(datos, tiene_pepas) que dado un tipo de fruta y un booleano, retorne las frutas que tienen o no tienen pepas (según el valor de tiene_pepas).

Por ejemplo, filtrar_por_pepa(datos, True) deberá retornar un arreglo con los diccionarios de: naranja, limon, frutilla y frambuesa.

3. La función conteo_colores(datos) que cree un diccionario que haga un conteo los colores de las frutas.

Para estos datos, la función debería retornar el siguiente diccionario:

{"amarillo": 3, "naranjo": 1, "rojo": 2}
`
In [3]:
def describir(datos):
    descripciones = []
    for fruta in datos:
        nombre = fruta["nombre"]
        genero = "La" if nombre[-1] == "a" else "El"
        presenta_pepas = "Presenta" if fruta["pepas"] else "No presenta"
        descripciones.append(f'{genero} {nombre} es una fruta de tipo {fruta["tipo"]} de color {fruta["color"]}. {presenta_pepas} pepas en su interior.')
    return descripciones


def filtrar_por_pepa(datos, tiene_pepas):
    return [fruta for fruta in datos if fruta["pepas"] == tiene_pepas]


def conteo_colores(datos):
    conteo = {}
    for fruta in datos:
        try:
            conteo[fruta["color"]] += 1
        except KeyError:
            conteo[fruta["color"]] = 1
    return conteo

El código de la siguiente celda presenta tests para probar sus funciones: Si la ejecución de esta celda no les levanta ninguna excepción, entonces sus funciones están implementadas según lo requerido. Si alguna falla, entonces deberán corregir los errores mostrados. Noten que los strings, diccionarios y en general, los objetos deben ser exactamente idénticos para que el test no falle.

Ejecuten esta celda para comprobar su código:

In [4]:
# ---------------------------------------------------------------------
# test descripciones
# note que las frutas deben estar en igual orden para que este assert no falle.

descripciones_esperadas = [
    "El limón es una fruta de tipo cítrica de color amarillo. Presenta pepas en su interior.",
    "La naranja es una fruta de tipo cítrica de color naranjo. Presenta pepas en su interior.",
    "El plátano es una fruta de tipo tropical de color amarillo. No presenta pepas en su interior.",
    "La piña es una fruta de tipo tropical de color amarillo. No presenta pepas en su interior.",
    "La frutilla es una fruta de tipo bosque de color rojo. Presenta pepas en su interior.",
    "La frambuesa es una fruta de tipo bosque de color rojo. Presenta pepas en su interior.",
]

assert descripciones_esperadas == describir(datos)


# ---------------------------------------------------------------------
# test filtro
frutas_con_pepa_esperadas = [
    {"nombre": "limón", "tipo": "cítrica", "color": "amarillo", "pepas": True},
    {"nombre": "naranja", "tipo": "cítrica", "color": "naranjo", "pepas": True},
    {"nombre": "frutilla", "tipo": "bosque", "color": "rojo", "pepas": True},
    {"nombre": "frambuesa", "tipo": "bosque", "color": "rojo", "pepas": True}
]

assert frutas_con_pepa_esperadas == filtrar_por_pepa(datos, True)

# ---------------------------------------------------------------------
# test conteo
conteo_esperado = {'amarillo': 3, 'naranjo': 1, 'rojo': 2}


assert conteo_esperado == conteo_colores(datos)

Implementaciones Funcionales [1.0 punto]¶

Implementar las funciones de la sección anterior de forma funcional (solo las primeras 2).

In [5]:
def describir_funcional(datos):

    def generar_descripcion(fruta):
        nombre = fruta["nombre"]
        genero = "La" if nombre[-1] == "a" else "El"
        presenta_pepas = "Presenta" if fruta["pepas"] else "No presenta"
        return f'{genero} {nombre} es una fruta de tipo {fruta["tipo"]} de color {fruta["color"]}. {presenta_pepas} pepas en su interior.'

    descripciones = list(map(generar_descripcion, datos))
    return descripciones

def filtrar_por_pepa_funcional(datos, tipo_de_dato):
    filtrados = list(filter(lambda fruta: fruta["pepas"] == tipo_de_dato, datos))
    return filtrados
In [6]:
assert descripciones_esperadas == describir_funcional(datos)
assert frutas_con_pepa_esperadas == filtrar_por_pepa_funcional(datos, True)

Parte 2: Electrodomésticos¶

En esta parte se solicitarán un par de clases que permitirá jugar con la Programación Orientada a Objetos.

[0.5] Clase Electrodoméstico¶

Defina la clase Electrodomestico que implemente:

  • Un constructor que defina un atributo de instancia llamado enchufado que almacene valores booleanos.
  • Un método llamado esta_enchufado(self) que levante una excepción Exception y que termine con la ejecución del programa cuando el atributo enchuado sea False. La exepción debe levantar el mensaje 'Alerta ⚠️: El electrodoméstico no está enchufado'
  • Un método llamado enchufar(self) que cambia el estado de enchufado a True.
In [7]:
class Electrodomestico:
    def __init__(self):
        self.enchufado = False

    def esta_enchufado(self):
        if not self.enchufado:
            raise Exception('Alerta ⚠️: El electrodoméstico no está enchufado')

    def enchufar(self):
        self.enchufado = True

Clase Jugera [1.5 puntos]¶

Implemente la clase Jugera que extiende Electrodomestico y que implemente:

  • Un constructor que tenga una lista de ingredientes frutales (llamado bandeja).
  • Un método llamado agregar_ingrediente(self, nueva_fruta) que dado una fruta, agregue esa fruta a la bandeja.

  • Un método llamado listar_ingredientes(self) que imprima (con print) los ingredientes actuales de la bandeja de la siguiente forma:

    Ingredientes en la bandeja: frutilla, frambuesa, piña.

De lo contrario si no tiene ingredientes imprima:

`Bandeja vacía`


Hint: Investigar el método join de un string para generar el string con los nombres de las frutas.

  • Un método llamado preparar_jugo(self) que:
    • Primero verifique que el electrodoméstico esté enchufado usando self.esta_enchufado().
    • Verifique que haya por lo menos un ingrediente en la bandeja. En el caso que no haya, levantar una excepción con contenido 'Error ❌: La bandeja no tiene ingredientes.'
    • Verifique que ninguna fruta tenga pepas. En el caso que haya alguna, imprimir (con print) el mensaje de advertencia 'Alerta ⚠️: El jugo puede contener restos de pepas.'. Puede usar la función definida en la sección anterior.
    • Genere un mensaje indicado Jugo de {nombres de las frutas separadas por una ,} listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️. (Hint: Usar nuevamente join).
    • Vacie la bandeja (es decir, eliminar todas las frutas de la bandeja).
    • Retorne el mensaje generado.
In [8]:
class Jugera(Electrodomestico):
    def __init__(self):
        super().__init__()
        self.bandeja = []

    def agregar_ingrediente(self, nueva_fruta):
        self.bandeja.append(nueva_fruta)

    def listar_ingredientes(self):
        if len(self.bandeja) == 0:
            print("Bandeja vacía")
        else:
            nombres = ", ".join(fruta["nombre"] for fruta in self.bandeja)
            print(f"Ingredientes en la bandeja: {nombres}.")

    def preparar_jugo(self):
        self.esta_enchufado()  # Cae error de no ser el caso
        if len(self.bandeja) < 1:
            raise Exception('Error ❌: La bandeja no tiene ingredientes.')
        if len(filtrar_por_pepa(datos, True)) > 0:
            print('Alerta ⚠️: El jugo puede contener restos de pepas.')
        nombres = ", ".join(fruta["nombre"] for fruta in self.bandeja)
        mensaje = f"Jugo de {nombres} listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️."
        self.bandeja = []
        return mensaje

Interacciones¶

Las siguientes celdas les permitirán probar las interacciones de esta clase. La ejecución es solo referencial y no lleva puntaje. La idea es que la utilice como guía para desarrollar la clase.

In [9]:
jugera = Jugera()

# Como no tenemos ingredientes, listar_ingredientes deberá imprimir 'Bandeja vacía'
jugera.listar_ingredientes()
Bandeja vacía
In [10]:
# Esta celda debería levantar una excepcion indicando que no está enchufada la jugera.
jugera.preparar_jugo()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Input In [10], in <cell line: 2>()
      1 # Esta celda debería levantar una excepcion indicando que no está enchufada la jugera.
----> 2 jugera.preparar_jugo()

Input In [8], in Jugera.preparar_jugo(self)
     16 def preparar_jugo(self):
---> 17     self.esta_enchufado()  # Cae error de no ser el caso
     18     if len(self.bandeja) < 1:
     19         raise Exception('Error ❌: La bandeja no tiene ingredientes.')

Input In [7], in Electrodomestico.esta_enchufado(self)
      5 def esta_enchufado(self):
      6     if not self.enchufado:
----> 7         raise Exception('Alerta ⚠️: El electrodoméstico no está enchufado')

Exception: Alerta ⚠️: El electrodoméstico no está enchufado
In [11]:
# Enchufamos el electrodoméstico
jugera.enchufar()
In [12]:
# Esta celda debería levantar ina excepción informandoles que la bandeja no tiene ingredientes.
jugera.preparar_jugo()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Input In [12], in <cell line: 2>()
      1 # Esta celda debería levantar ina excepción informandoles que la bandeja no tiene ingredientes.
----> 2 jugera.preparar_jugo()

Input In [8], in Jugera.preparar_jugo(self)
     17 self.esta_enchufado()  # Cae error de no ser el caso
     18 if len(self.bandeja) < 1:
---> 19     raise Exception('Error ❌: La bandeja no tiene ingredientes.')
     20 if len(filtrar_por_pepa(datos, True)) > 0:
     21     print('Alerta ⚠️: El jugo puede contener restos de pepas.')

Exception: Error ❌: La bandeja no tiene ingredientes.
In [13]:
# Agregamos algunos ingredientes
jugera.agregar_ingrediente(naranja)
jugera.agregar_ingrediente(platano)

# Y los listamos (debería imprimir: 'Ingredientes en la bandeja: naranja, plátano')
jugera.listar_ingredientes()
Ingredientes en la bandeja: naranja, plátano.
In [14]:
# Preparamos el jugo: 'Jugo de naranja, plátano listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️.'
jugera.preparar_jugo()
Alerta ⚠️: El jugo puede contener restos de pepas.
Out[14]:
'Jugo de naranja, plátano listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️.'
In [15]:
# Una vez preparado el jugo, debería vaciarse la bandeja (imprimir Bandeja vacía)
jugera.listar_ingredientes()
Bandeja vacía

Clase Jugera + Properties [1.0]¶

Implementar bandeja usando una property que permita setear una nueva_bandeja como bandeja según las siguientes condiciones:

  • Compruebe que nueva_bandeja sea una lista. En caso contrario, levante una excepción.
  • No permita agregar más de 3 ingredientes a la bandeja a la vez. Si se entregan más de 3 frutas, se levante una excepción.
  • Se compruebe que todos los elementos del arreglo sean frutas. Para esto, por cada fruta compruebe que:
    1. La fruta sea diccionario.
    2. El diccionario entregado tenga las llaves nombre, tipo, color y pepas.
In [16]:
class Jugera(Electrodomestico):
    def __init__(self):
        super().__init__()
        self._bandeja = []

    @property
    def bandeja(self):
        return self._bandeja

    @bandeja.setter
    def bandeja(self, nueva_bandeja):
        if not isinstance(nueva_bandeja, list):
            raise TypeError(f"Bandeja nueva es de tipo {type(nueva_bandeja)} en vez de list.")
        n = len(nueva_bandeja)
        if n > 3:
            raise Exception(f"Nueva bandeja es tiene {n} elementos, se espera que tenga a lo más 3.")
        for fruta in nueva_bandeja:
            self.agregar_ingrediente(fruta)

    def agregar_ingrediente(self, nueva_fruta):
        if not isinstance(nueva_fruta, dict):
            raise TypeError(f"Fruta {nueva_fruta} es de tipo {type(nueva_fruta)} en vez de dict")
        for llave in ("nombre", "tipo", "color", "pepas"):
            if llave not in nueva_fruta.keys():
                raise KeyError(f"Fruta {nueva_fruta} no tiene llave {llave}.")
        self._bandeja.append(nueva_fruta)

    def listar_ingredientes(self):
        if len(self._bandeja) == 0:
            print("Bandeja vacía")
        else:
            nombres = ", ".join(fruta["nombre"] for fruta in self._bandeja)
            print(f"Ingredientes en la bandeja: {nombres}.")

    def preparar_jugo(self):
        self.esta_enchufado()  # Cae error de no ser el caso
        if len(self._bandeja) < 1:
            raise Exception('Error ❌: La bandeja no tiene ingredientes.')
        if len(filtrar_por_pepa(datos, True)) > 0:
            print('Alerta ⚠️: El jugo puede contener restos de pepas.')
        nombres = ", ".join(fruta["nombre"] for fruta in self._bandeja)
        mensaje = f"Jugo de {nombres} listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️."
        self._bandeja = []
        return mensaje

Interacciones¶

Las siguientes celdas les permitirán probar las interacciones de esta clase. La ejecución es solo referencial y no lleva puntaje. La idea es que la utilice como guía para desarrollar la clase.

In [17]:
jugera_2 = Jugera()

jugera_2.listar_ingredientes()
Bandeja vacía
In [18]:
jugera_2.preparar_jugo()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Input In [18], in <cell line: 1>()
----> 1 jugera_2.preparar_jugo()

Input In [16], in Jugera.preparar_jugo(self)
     35 def preparar_jugo(self):
---> 36     self.esta_enchufado()  # Cae error de no ser el caso
     37     if len(self._bandeja) < 1:
     38         raise Exception('Error ❌: La bandeja no tiene ingredientes.')

Input In [7], in Electrodomestico.esta_enchufado(self)
      5 def esta_enchufado(self):
      6     if not self.enchufado:
----> 7         raise Exception('Alerta ⚠️: El electrodoméstico no está enchufado')

Exception: Alerta ⚠️: El electrodoméstico no está enchufado
In [19]:
# Enchufamos el electrodoméstico
jugera_2.enchufar()
In [20]:
# Esta celda debería levantar ina excepción informandoles que la bandeja no tiene ingredientes.
jugera_2.preparar_jugo()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Input In [20], in <cell line: 2>()
      1 # Esta celda debería levantar ina excepción informandoles que la bandeja no tiene ingredientes.
----> 2 jugera_2.preparar_jugo()

Input In [16], in Jugera.preparar_jugo(self)
     36 self.esta_enchufado()  # Cae error de no ser el caso
     37 if len(self._bandeja) < 1:
---> 38     raise Exception('Error ❌: La bandeja no tiene ingredientes.')
     39 if len(filtrar_por_pepa(datos, True)) > 0:
     40     print('Alerta ⚠️: El jugo puede contener restos de pepas.')

Exception: Error ❌: La bandeja no tiene ingredientes.
In [21]:
# Agregamos algunos ingredientes (en este caso, como son más de 3, fallará)
jugera_2.bandeja = [naranja, platano, frutilla, limon]
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Input In [21], in <cell line: 2>()
      1 # Agregamos algunos ingredientes (en este caso, como son más de 3, fallará)
----> 2 jugera_2.bandeja = [naranja, platano, frutilla, limon]

Input In [16], in Jugera.bandeja(self, nueva_bandeja)
     14 n = len(nueva_bandeja)
     15 if n > 3:
---> 16     raise Exception(f"Nueva bandeja es tiene {n} elementos, se espera que tenga a lo más 3.")
     17 for fruta in nueva_bandeja:
     18     self.agregar_ingrediente(fruta)

Exception: Nueva bandeja es tiene 4 elementos, se espera que tenga a lo más 3.
In [22]:
# Agregamos algunos ingredientes 
# (en este caso debería fallar, ya que estamos entregando un string en el primer lugar)
jugera_2.bandeja = ["naranja", platano, frutilla]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [22], in <cell line: 3>()
      1 # Agregamos algunos ingredientes 
      2 # (en este caso debería fallar, ya que estamos entregando un string en el primer lugar)
----> 3 jugera_2.bandeja = ["naranja", platano, frutilla]

Input In [16], in Jugera.bandeja(self, nueva_bandeja)
     16     raise Exception(f"Nueva bandeja es tiene {n} elementos, se espera que tenga a lo más 3.")
     17 for fruta in nueva_bandeja:
---> 18     self.agregar_ingrediente(fruta)

Input In [16], in Jugera.agregar_ingrediente(self, nueva_fruta)
     20 def agregar_ingrediente(self, nueva_fruta):
     21     if not isinstance(nueva_fruta, dict):
---> 22         raise TypeError(f"Fruta {nueva_fruta} es de tipo {type(nueva_fruta)} en vez de dict")
     23     for llave in ("nombre", "tipo", "color", "pepas"):
     24         if llave not in nueva_fruta.keys():

TypeError: Fruta naranja es de tipo <class 'str'> en vez de dict
In [23]:
# Agregamos algunos ingredientes 
# (en este caso debería fallar, ya que arándando tiene solo la llave nombre)
jugera_2.bandeja = [{'nombre': 'arándano'}, platano, frutilla]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Input In [23], in <cell line: 3>()
      1 # Agregamos algunos ingredientes 
      2 # (en este caso debería fallar, ya que arándando tiene solo la llave nombre)
----> 3 jugera_2.bandeja = [{'nombre': 'arándano'}, platano, frutilla]

Input In [16], in Jugera.bandeja(self, nueva_bandeja)
     16     raise Exception(f"Nueva bandeja es tiene {n} elementos, se espera que tenga a lo más 3.")
     17 for fruta in nueva_bandeja:
---> 18     self.agregar_ingrediente(fruta)

Input In [16], in Jugera.agregar_ingrediente(self, nueva_fruta)
     23 for llave in ("nombre", "tipo", "color", "pepas"):
     24     if llave not in nueva_fruta.keys():
---> 25         raise KeyError(f"Fruta {nueva_fruta} no tiene llave {llave}.")
     26 self._bandeja.append(nueva_fruta)

KeyError: "Fruta {'nombre': 'arándano'} no tiene llave tipo."
In [24]:
# Agregamos algunos ingredientes (en este caso, como son 3, debería funcionar)
jugera_2.bandeja = [naranja, platano, frutilla]

# Y los listamos (debería imprimir: 'Ingredientes en la bandeja: naranja, plátano')
jugera_2.listar_ingredientes()
Ingredientes en la bandeja: naranja, plátano, frutilla.
In [25]:
# Una vez preparado el jugo, imprima el contenido del jugo y si una alerta, 
# en el caso que el jugo tenga pepas.
jugera_2.preparar_jugo()
Alerta ⚠️: El jugo puede contener restos de pepas.
Out[25]:
'Jugo de naranja, plátano, frutilla listo. 🏖️🥤 Que lo disfrutes!!! 🥤🏖️.'
In [26]:
jugera_2._bandeja
Out[26]:
[]

Created in deepnote.com Created in Deepnote