{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "view-in-github", "colab_type": "text" }, "source": [ "\"Open" ] }, { "cell_type": "markdown", "id": "055fc0df", "metadata": { "id": "055fc0df" }, "source": [ "# Reto 1\n", "* Crear la clase Depósito bancario y dotarla de una serie de atributos y métodos.\n", "* Algunos deben ser atributos de instancia y otros atributos de clase.\n", " - duración del depósito en días\n", " - cuantía en euros, importe invertido\n", " - tipo de interés efectivo anual\n", " - usando una variable booleana, indicar si el depósito ha llegado a su vencimiento\n", "* Crear tres instancias" ] }, { "cell_type": "markdown", "id": "5f507219", "metadata": { "id": "5f507219" }, "source": [ "## Solución\n", "- Los **atributos de clase** son compartidos por todas las instancias de esa clase.\n", "- Los **atributos de instancia** son propios de cada objeto instanciado en esa clase." ] }, { "cell_type": "code", "execution_count": null, "id": "acc4e50d", "metadata": { "id": "acc4e50d" }, "outputs": [], "source": [ "class Deposito:\n", " def __init__(self, duracion, cuantia, tasa, activo): # método constructor con los atributos de instancia\n", " self.duracion = duracion # duración del depósito bancario en días\n", " self.cuantia = cuantia # importe en euros\n", " self.tasa = tasa # tipo de interés efectivo anual, expresado en tanto por uno\n", " self.activo = activo # True si aún no ha llegado a vencimiento, False si es un depósito antiguo ya vencido\n", " self.moneda = \"€\" # atributo de clase\n", " self.interes = \"efectivo anual\" # atributo de clase\n", " \n", " def montante(self): # un método que calcula el montante final a devolver\n", " return self.cuantia * (1 + self.tasa)**(self.duracion/365)\n", " \n", " def vencimiento(self):\n", " if self.activo: return \"no\"\n", " else: return \"si\"\n", " \n", "d1 = Deposito(480, 50_000, 0.01, False) # instanciamos los tres depósitos\n", "d2 = Deposito(180, 10_000, 0.02, True)\n", "d3 = Deposito(360, 120_000, 0.03, True)\n", "\n", "print(f\"El importe invertido en el depósito d1 es {d1.cuantia} {d1.moneda}.\") # probando el atributo cuantía\n", "print(f\"El montante a devolver en el depósito d1 asciende a {round(d1.montante(),2)} {d1.moneda}.\") # el método montante\n", "print()\n", "print(f\"El depósito d1 ha llegado a vencimiento: {not d1.activo}\")\n", "print(f\"El depósito d2 ha llegado a vencimiento: {not d2.activo}\")\n", "print(f\"El depósito d3 ha llegado a vencimiento: {not d3.activo}\")\n", "print()\n", "print(\"OTRA FORMA DE EXPRESARLO (usando el método vencimiento)\")\n", "print(f\"\\tEl depósito d1 {d1.vencimiento()} ha llegado a vencimiento.\")\n", "print(f\"\\tEl depósito d2 {d2.vencimiento()} ha llegado a vencimiento.\")\n", "print(f\"\\tEl depósito d3 {d3.vencimiento()} ha llegado a vencimiento.\")" ] }, { "cell_type": "markdown", "id": "e54ee82d", "metadata": { "id": "e54ee82d" }, "source": [ "# Reto 2\n", "Sumar una serie indeterminada de sumandos en una función." ] }, { "cell_type": "code", "execution_count": null, "id": "b4680f69", "metadata": { "id": "b4680f69" }, "outputs": [], "source": [ "def suma(a,b): # esto es una suma solo de dos argumentos\n", " return a+b\n", "\n", "print(suma(5,4))\n", "\n", "def sumar(*valores): # en este caso, al poner asterisco, podemos sumar un número indeterminado de argumentos\n", " total = 0\n", " for valor in valores:\n", " total += valor\n", " return total \n", "\n", "print(sumar(10,20,30)) # podemos sumar tres argumentos\n", "print(sumar(1,2,3,4,5,6)) # podemos sumar seis argumentos, o el número que queramos usando la misma función" ] }, { "cell_type": "markdown", "id": "36e8bb67", "metadata": { "id": "36e8bb67" }, "source": [ "# Reto 3\n", "Ordenar un diccionario por sus valores de mayor a menor." ] }, { "cell_type": "code", "execution_count": null, "id": "bc52f7a4", "metadata": { "id": "bc52f7a4" }, "outputs": [], "source": [ "dict = {\"C#\":100, \"Python\":900, \"R\":700, \"Java\":300} \n", "\n", "lista = sorted(dict.items(), key=lambda x: x[1], reverse=True)\n", "\n", "for i in lista:\n", " print(i[0],\"\\t\", i[1])" ] }, { "cell_type": "markdown", "id": "0a13e826", "metadata": { "id": "0a13e826" }, "source": [ "# Reto 4\n", "- Crear la clase País con un atributo que sea el PIB (Producto Interior Bruto). \n", "- Instanciar varios objetos de tipo *pais*, con diferentes valores de su PIB.\n", "- Ordenar los países por su PIB.\n", "\n", "Fuente: [EEUU y la UE ante el desafío geoestratégico de China y Rusia](https://www.eleconomista.es/opinion-blogs/noticias/11057711/02/21/EEUU-y-la-UE-ante-el-desafio-geoestrategico-de-China-y-Rusia.html)" ] }, { "cell_type": "code", "execution_count": null, "id": "2e3f6686", "metadata": { "id": "2e3f6686" }, "outputs": [], "source": [ "class Pais:\n", " def __init__(self,nombre, pib):\n", " self.nombre = nombre\n", " self.pib = pib\n", " \n", " def ordena(self,*paises): # El asterisco toma un nº indeterminado de argumentos\n", " lista_nombre = [self.nombre]\n", " lista_pib = [self.pib]\n", " for pais in paises:\n", " lista_nombre.append(pais.nombre)\n", " lista_pib.append(pais.pib)\n", " d = dict(zip(lista_nombre, lista_pib)) # diccionario\n", " #print(d)\n", " lista = sorted(d.items(), key=lambda x: x[1], reverse=True)\n", " for i in lista:\n", " print(i[0],\"\\t\", i[1])\n", " \n", "p1 = Pais(\"China\", 14.8) # instanciamos un objeto País\n", "p2 = Pais(\"UE\", 13.9) # instanciamos otro objeto País\n", "p3 = Pais(\"EEUU\", 20.8) # instanciamos el tercer objeto País\n", "\n", "print(\"{:>10}\".format(\"RANKING\"))\n", "print(\"=\"*14)\n", "p1.ordena(p2,p3) # ordena p1, p2 y p3\n", "#p2.ordena(p1,p3) # hace exactamente lo mismo\n", "#p3.ordena(p2,p3) # hace exactamente lo mismo" ] }, { "cell_type": "markdown", "id": "8536659b", "metadata": { "id": "8536659b" }, "source": [ "# Reto 5\n", "Usar el doble asterisco." ] }, { "cell_type": "markdown", "id": "677f4b62", "metadata": { "id": "677f4b62" }, "source": [ "## Solución\n", "El doble asterisco se utiliza para tratar un número indeterminado de elementos de un diccionario. \n", "El doble asterisco se utiliza para desempaquetar diccionarios. \n", "Al tratarse de un diccionario, el orden de los parámetros no importa." ] }, { "cell_type": "code", "execution_count": null, "id": "4757bcb5", "metadata": { "id": "4757bcb5" }, "outputs": [], "source": [ "def letras(**kwargs):\n", " return kwargs\n", "\n", "letras(A=1, B=2, C=3, D=4) # el resultado es un diccionario {'A': 1, 'B': 2, 'C': 3, 'D': 4}" ] }, { "cell_type": "code", "execution_count": null, "id": "39c60838", "metadata": { "id": "39c60838" }, "outputs": [], "source": [ "def poblacion(**kwargs):\n", " for key, value in kwargs.items():\n", " print(f\"En 2012 La población de {key} se estima que fue de {value} personas.\")\n", "\n", "poblacion(China=1_343_239_923, India=1_205_073_612, UE=443_108_346, EEUU=313_847_465)" ] }, { "cell_type": "markdown", "id": "9f90e3db", "metadata": { "id": "9f90e3db" }, "source": [ "# Reto 6\n", "Usar el doble asterisco en POO." ] }, { "cell_type": "code", "execution_count": null, "id": "bff8e16c", "metadata": { "id": "bff8e16c" }, "outputs": [], "source": [ "#!/usr/bin/env python\n", "# -*- coding: utf-8 -*-\n", "\n", "class Persona:\n", " def __init__(self, edad, nombre):\n", " self.edad = edad\n", " self.nombre = nombre\n", " print(f\"Hola, soy {self.nombre} de {self.edad} años.\")\n", " def hablar(self, **palabras):\n", " for palabra in palabras:\n", " print(f\"{self.nombre}: {palabras[palabra]}\")\n", "juanjo = Persona(nombre=\"Juan José\", edad=30)\n", "juanjo.hablar(t1=\"hola\", t2=\"encantado de estar aquí\")" ] }, { "cell_type": "markdown", "id": "bce5a903", "metadata": { "id": "bce5a903" }, "source": [ "# Reto 7\n", "Redondear y poner separador de miles." ] }, { "cell_type": "markdown", "id": "92f01fb5", "metadata": { "id": "92f01fb5" }, "source": [ "## Solución\n", "[Separador de miles con punto y coma decimal en Python](https://altocodigo.blogspot.com/2019/06/separador-de-miles-con-punto-y-coma.html)" ] }, { "cell_type": "code", "execution_count": null, "id": "3e3c60b3", "metadata": { "id": "3e3c60b3" }, "outputs": [], "source": [ "a = 1234.576\n", "print(a)\n", "b = round(a,2) # redondeando a dos decimales\n", "print(b)\n", "print(\"{:,}\".format(b)) # formato inglés\n", "print(\"{:,}\".format(b).replace(',','~').replace('.',',').replace('~','.')) # formato español" ] }, { "cell_type": "markdown", "id": "bacecec2", "metadata": { "id": "bacecec2" }, "source": [ "# Reto 8\n", "Crear una clase con algunos atributos y procurar que sean privados.\n", "\n", "Crear la clase Scoring para determinar si se concederá o no un préstamo en función de una serie de características personales y financieras del cliente." ] }, { "cell_type": "markdown", "id": "423c363e", "metadata": { "id": "423c363e" }, "source": [ "## Solución\n", "El **Credit Scoring** es el conjunto de técnicas estadísticas que permiten evaluar una solicitud de crédito y determinar si la operación financiera llegará a ser viable o no.\n", "\n", "Veamos dos métodos de resolución del reto.\n", "- Método 1, intentando que los atributos sean privados con ```__atributo```.\n", "- Método 2, haciendo privados los atributos usando decoradores con ```@property```" ] }, { "cell_type": "markdown", "id": "0d90c65b", "metadata": { "id": "0d90c65b" }, "source": [ "## Método 1\n", "Usando un **prefijo de doble barra baja** en los nombres de los atributos se intenta que los atributos así definidos sean privados. Sirve para avisar a otros programadores o a nosotros mismos, en el futuro cuando revisemos el código, que ese atributo queremos que no sea accesible desde fuera de la propia clase.\n", "\n", "La realidad es que esto solo es un intento, una declaración de intenciones, ya que un programador experimentado podría acceder a esos atributos desde fuera de la clase. Por eso decimos que es un 'intento' que pretende hacer privados estos atributos." ] }, { "cell_type": "code", "execution_count": null, "id": "0304e9ed", "metadata": { "id": "0304e9ed" }, "outputs": [], "source": [ "class Scoring:\n", " def __init__(self, nombre_cliente, edad, importe_solicitado, patrimonio, deudas, ingresos_anuales):\n", " self.nombre = nombre_cliente # atributo público\n", " self.edad = edad # atributo público\n", " self.__importe = importe_solicitado # atributo que pretende ser privado\n", " self.__patrimonio = patrimonio # atributo que pretende ser privado\n", " self.__deudas = deudas # atributo que pretende ser privado\n", " self.__ingresos = ingresos_anuales # atributo que pretende ser privado\n", " \n", " def importe_concedido(self):\n", " if 30 <= self.edad <= 50:\n", " coef = 0.5 # en ese rango de edad se concede el 50% del patrimonio neto disponible estimado\n", " else:\n", " coef = 0.4 # fuera de ese rango de edad se concede solo el 40%\n", " concedido = coef*((self.__patrimonio-self.__deudas)+self.__ingresos-self.__importe)\n", " concedido = min(concedido,self.__importe) # no se concede más del importe solicitado\n", " concedido = max(concedido, 0) # la cantidad concedida nunca es negativa\n", " concedido = round(concedido) # redondeamos a cero decimales\n", " return \"{:,} €\".format(concedido) # ponemos separador de miles\n", " \n", "c1 = Scoring(\"Ana\", 35, 30000, 200000, 150000, 35000) # instanciamos el objeto c1 (cliente 1)\n", "\n", "print(f\"{c1.nombre} después del estudio, hemos podido concederle {c1.importe_concedido()}\")\n", "\n", "#c1.nombre #es un atributo público, podemos acceder a él desde fuera de la clase\n", "#c1.importe_concedido() #es un método público. Al ser método lleva los paréntesis ()" ] }, { "cell_type": "code", "execution_count": null, "id": "900848b9", "metadata": { "id": "900848b9" }, "outputs": [], "source": [ "# si intentamos acceder al patrimonio de Ana desde fuera de la clase nos dará error\n", "# pero si hemos podido acceder a su nombre porque no lleva los dos guiones bajos precediendo al atributo\n", "#c1.__patrimonio # para ver el error quite el símbolo # del comentario\n", "#c1.patrimonio # tampoco funciona, da error\n", "\n", "print(c1.nombre) # si puedo acceder al nombre pq no es privado\n", "print(c1.edad) # si puedo acceder a la edad pq no es privado\n", "\n", "# pero un programador experimentado podría saber que el atributo patrimonio ahora se ha renombrado como\n", "# _Scoring__patrimonio y podría consultarlo, obteniendo una información sensible que no debiera ser pública\n", "c1._Scoring__patrimonio # 200000, vemos que ahora funciona y no da error" ] }, { "cell_type": "code", "execution_count": null, "id": "b8c41b25", "metadata": { "id": "b8c41b25" }, "outputs": [], "source": [ "dir(c1) # listado con los nombres de atributos y métodos del objeto c1" ] }, { "cell_type": "code", "execution_count": null, "id": "715cb373", "metadata": { "id": "715cb373" }, "outputs": [], "source": [ "# podemos acceder a la variable patrimonio y modificarlo desde fuera, haciendo trampas\n", "c1._Scoring__patrimonio = 500000\n", "c1._Scoring__patrimonio" ] }, { "cell_type": "code", "execution_count": null, "id": "cb6ae716", "metadata": { "id": "cb6ae716" }, "outputs": [], "source": [ "# de esta forma si ahora volvemos a calcular el importe_concedido veremos que sube nuestro límite\n", "c1.importe_concedido() # 30000, ahora nos pueden conceder un mayor importe en el préstamo solicitado, TRAMPA" ] }, { "cell_type": "markdown", "id": "5f6b80ab", "metadata": { "id": "5f6b80ab" }, "source": [ "## Método 2\n", "Haciendo privados los atributos usando decoradores con ```@property```" ] }, { "cell_type": "code", "execution_count": null, "id": "8cfc1b17", "metadata": { "id": "8cfc1b17" }, "outputs": [], "source": [ "class Scoring:\n", " def __init__(self, nombre_cliente, edad, importe_solicitado, patrimonio, deudas, ingresos_anuales):\n", " self.nombre = nombre_cliente\n", " self.edad = edad\n", " self.__importe = importe_solicitado\n", " self.__patrimonio = patrimonio\n", " self.__deudas = deudas\n", " self.__ingresos = ingresos_anuales\n", " \n", " @property # usando un decorador como método getter\n", " def patrimonio(self):\n", " print(\"Se ha llamado al método getter\")\n", " return \"El patrimonio es privado\"\n", " \n", " @patrimonio.setter # creando el método setter\n", " def patrimonio(self, valor):\n", " self.__patrimonio = valor\n", " print(\"El patrimonio ha sido modificado a\", self.__patrimonio) # opcionalmente ponemos un print informativo\n", " print(\"Se ha enviado un informe al supervisor para comprobar este cambio.\")\n", " \n", " def importe_concedido(self):\n", " if 30 <= self.edad <= 50:\n", " coef = 0.5\n", " else:\n", " coef = 0.4\n", " concedido = coef*((self.__patrimonio-self.__deudas)+self.__ingresos-self.__importe)\n", " concedido = min(concedido,self.__importe)\n", " concedido = max(concedido, 0)\n", " concedido = round(concedido)\n", " return \"{:,} €\".format(concedido)\n", " \n", "c1 = Scoring(\"Ana\", 35, 30000, 200000, 150000, 35000)\n", "print(f\"{c1.nombre} después del estudio, hemos podido concederle {c1.importe_concedido()}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "873800ae", "metadata": { "id": "873800ae" }, "outputs": [], "source": [ "# accedemos al método setter para cambiar el patrimonio\n", "c1.patrimonio = 170000 # usamos directamente el atributo 'patrimonio' y nos deja\n", "# muestra la frase optativa informando de que el patrimonio ha sido cambiado\n", "# el setter podría introducir más lógica de programación para controlar los cambios que se pretendan introducir" ] }, { "cell_type": "code", "execution_count": null, "id": "2c9ed8f1", "metadata": { "id": "2c9ed8f1" }, "outputs": [], "source": [ "c1.importe_concedido()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.11" }, "colab": { "name": "caso2.02.ipynb", "provenance": [], "include_colab_link": true } }, "nbformat": 4, "nbformat_minor": 5 }