🪞 Autoevaluación
1 Fundamentos
¿Qué diferencia hay entre una clase y un objeto? ¿Y entre un método y una función?
¿Qué ocurre al ejecutar la siguiente definición? ¿Se crea algún objeto de tipo
Budinen ese momento?class Budin: def __init__(self): print("Creando budín")¿En qué momento se ejecuta
__init__? ¿Cuál es su propósito? ¿Por qué no debería contener unreturn?¿Qué representa
selfdentro de un método? ¿Por qué no se debe pasar explícitamente al llamar a un método desde un objeto?Considere la siguiente clase:
class Budin: def __init__(self, base): self.base = base- ¿Cuál es la diferencia entre
baseyself.base? - ¿Qué devuelve
Budin("vainilla").base? - ¿Qué ocurriría si se intentara acceder a
Budin("vainilla").cobertura?
- ¿Cuál es la diferencia entre
¿Por qué los dos objetos siguientes pueden tener valores distintos para el atributo
base?b1 = Budin("vainilla") b2 = Budin("chocolate")¿Cuántos objetos crea el siguiente programa?
Budin("vainilla") Budin("vainilla")¿Qué problema puede generar agregar un atributo solo a una instancia?
b1.cobertura = "chocolate"¿Cómo podría evitarse usando un valor por defecto en
__init__?¿En qué casos tiene sentido que un método devuelva
self? De un ejemplo.Analice el método
transferirdeCuentaBancaria. ¿Qué debería ocurrir si se intenta transferir más dinero del que hay disponible? ¿El valor que devuelve el método representa correctamente el resultado de la operación?
2 Encapsulamiento
¿Qué es el encapsulamiento? Explíquelo usando la diferencia entre el interior y el exterior de un objeto.
¿Por qué una función también puede considerarse una forma de encapsulamiento?
¿Qué información debería poder obtener una persona que usa una clase sin necesidad de leer su implementación?
¿Para qué sirven los docstrings? ¿Qué información conviene incluir en el docstring de un método?
¿Qué muestra
help(CuentaBancaria)? ¿En qué se diferencia de ejecutarhelp(CuentaBancaria.depositar)?¿Por qué no se obtiene un error al ejecutar el siguiente programa?
def saludar(nombre: str) -> str: return f"¡Hola, {nombre}!" saludar(128)¿Qué son un getter y un setter? ¿Qué ventaja ofrece un setter frente a asignar directamente un atributo?
¿Qué comunica por convención un atributo cuyo nombre comienza con
_? ¿Python impide acceder o modificar ese atributo desde afuera?¿Qué diferencia hay entre
_nombrey__nombreen una clase de Python?Considere el siguiente código:
e = Estudiante("María") e.__nombre = "Juana"¿Por qué
e.getNombre()puede seguir devolviendo"María"? ¿Cómo se llama el mecanismo involucrado?¿Cuándo se ejecuta el método decorado con
@nombre.setter? ¿Qué sucede si el setter rechaza el nuevo valor?¿Cuál es la diferencia entre un atributo de instancia y un atributo de clase? ¿Qué tipo de información conviene guardar en cada uno?
¿Qué diferencia hay entre un método de instancia y un método de clase? ¿Qué representa
clsen un método decorado con@classmethod?¿Por qué
Estudiante.desde_texto("Ana, 2024")puede considerarse un constructor alternativo?
3 Herencia
¿Qué es la herencia? ¿A qué aspectos de la clase padre puede acceder la clase hija?
Si
ces una instancia deCuadrado, ¿a qué evalúan las siguientes expresiones? Justifique.class Rectangulo: pass class Cuadrado(Rectangulo): pass isinstance(c, Cuadrado) isinstance(c, Rectangulo) isinstance(c, object)¿Qué es la sobreescritura de métodos? ¿Cuándo tiene sentido usarla?
¿Qué es una clase abstracta? ¿Y un método abstracto? ¿Cuándo conviene utilizarlos?
¿Qué es la herencia múltiple?
¿Qué sucede si una clase hija hereda de dos clases padres que implementan un método con el mismo nombre?
¿Para qué sirve
super()?
4 Polimorfismo
¿Qué significa polimorfismo en el contexto de programación?
¿Por qué el siguiente bucle no necesita preguntar si cada mascota es un
Perro, unGatoo unPajaro?for mascota in mascotas: mascota.hablar()¿Es necesario que dos clases compartan una misma clase padre para que puedan usarse polimórficamente? Piense en el ejemplo de rectángulos y círculos que ambos implementan
area.¿Por qué se puede ordenar una lista que contiene rectángulos y círculos con esta expresión?
sorted(formas, key=lambda forma: forma.area())¿Qué método mágico se ejecuta al usar el operador
==? ¿Por qué dos objetosRectangulo(3, 2)distintos danFalsesi no se implementa ese método?Relacione cada operación con el método mágico correspondiente:
a == b a < b a + b a / b¿Qué diferencia hay entre
__repr__y__str__? ¿Cuándo se utilizan automáticamente por Python? ¿Cómo podemos utilizarlos manualmente?¿Por qué la implementación de
__add__deVectordevuelve un nuevoVectoren lugar de modificar alguno de los dos vectores originales?¿Qué significa “sobrecarga de operadores”?
Elija uno de los métodos
__len__,__contains__,__call__o__bool__. ¿Qué operación de Python habilitaría al implementarlo en una clase?
5 Excepciones
¿Qué indica un
NameError? ¿Por qué un nombre de variable escrito incorrectamente puede producir esta excepción?Sin ejecutar las expresiones, indique qué excepción produciría cada una y explique su causa.
5 + "5" numeros = [10, 20, 30] numeros[5] numero = 42 numero.append(5)¿Qué función cumplen los bloques
tryyexcept? ¿Qué ocurre si dentro deltryse lanza una excepción cuyo tipo no coincide con ninguno de los bloquesexcept?Considere el siguiente programa:
try: numero = int(texto) resultado = 100 / numero except ValueError: print("El texto no representa un número entero") except ZeroDivisionError: print("No se puede dividir por cero") else: print("Resultado:", resultado) finally: print("Fin del intento")¿Qué bloques se ejecutan y qué se imprime cuando
textovale"diez","0"y"4"?¿Cuándo se ejecuta un bloque
finally? ¿Por qué resulta útil para liberar recursos incluso cuando una excepción no es capturada?¿Qué hace la instrucción
raise? ¿Qué sucede con las instrucciones posteriores de la función cuando se ejecuta?Al lanzar una excepción, ¿por qué suele ser preferible lanzar un
ValueErrorespecífico en lugar de unExceptiongeneral?¿Qué efecto tiene ejecutar
raisesin argumentos dentro de un bloqueexcept? ¿En qué se diferencia de lanzar una excepción nueva?¿Para qué se utiliza
raise NuevaExcepcion(...) from error_original? ¿Qué información conserva en el traceback?¿Por qué una excepción personalizada suele heredar de
Exceptiono de alguna de sus subclases? ¿Qué ventaja ofrece crear una jerarquía de excepciones relacionadas?
6 Para pensar…
Sin ejecutar el programa, anote qué debería imprimir cada
print. Luego ejecútelo y explique qué objetos cambiaron y cuáles no. Useidsi lo considera útil.class ListaDeCompras: def __init__(self, productos): self.productos = productos def agregar(self, producto): self.productos.append(producto) return self feria = ["naranjas"] lunes = ListaDeCompras(feria) martes = ListaDeCompras(feria) lunes.agregar("pan") print(lunes.productos) print(martes.productos) print(feria)¿Es deseable el comportamiento del programa? Proponga un cambio en
__init__para que cada objeto conserve su propia lista, sin impedir que se use una lista como argumento de entrada.Una persona escribe esta clase para contar cuantas canciones agregó a cada lista:
class Playlist: canciones = [] def agregar(self, cancion): self.canciones.append(cancion) viaje = Playlist() estudio = Playlist() viaje.agregar("Justo que te vas") estudio.agregar("Se vos") print(viaje.canciones) print(estudio.canciones)Ejecútelo. ¿Qué error de diseño revela? Reescriba la clase para que las canciones sean un atributo de instancia y agregue, además, un atributo de clase que cuente cuántas
Playlistse crearon.Analice este intento de implementar un contador encadenable:
class Marcador: def __init__(self): self.puntos = 0 def sumar(self, cantidad): self.puntos += cantidad def reiniciar(self): self.puntos = 0 return self resultado = Marcador().sumar(3).reiniciar().sumar(2)¿En qué expresión aparece el primer error y por qué? Corrija el diseño para que la última línea sea válida.
Ejecute este programa con los valores
-1,0,3,Truey2.5como argumentos deBoleto. ¿Cuáles deberían aceptarse si el atributo representa una cantidad de viajes disponibles? Modifique el setter para hacer cumplir ese contrato y para que no se pueda llegar a un estado inválido después de crear el objeto.class Boleto: def __init__(self, viajes): self.viajes = viajes @property def viajes(self): return self._viajes @viajes.setter def viajes(self, valor): self._viajes = valor¿Qué condiciones debe cumplir
valorpara ser aceptado en el setter? ¿Qué mensaje daría en el caso de que no se cumplan? Justifique específicamente el caso deTrue, que en Python es una instancia deint.La clase siguiente expone una propiedad de solo lectura, pero todavía permite modificar el estado interno desde afuera:
class Equipo: def __init__(self, integrantes): self._integrantes = integrantes @property def integrantes(self): return self._integrantes equipo = Equipo(["Nora", "Lautaro"]) equipo.integrantes.pop() print(equipo.integrantes)Explique por qué no intervino ningún setter. Cambie la implementación para que el código cliente pueda consultar los integrantes pero no alterar la colección interna por accidente. Verifique también qué ocurre si se modifica la lista original que se pasó al constructor.
Un sitio recibe una fecha de publicación en dos formatos. Complete el método de clase y decida cómo debería responder ante una entrada mal formada.
class Publicacion: def __init__(self, titulo, anio): self.titulo = titulo self.anio = anio @classmethod def desde_texto(cls, texto): # admite, por ejemplo, "La invención de Morel | 1940" passLuego compare
Publicacion.desde_texto(...)con una función externa que construye y devuelve unaPublicacion: ¿por qué el método de clase es una mejor decisión si después aparece la subclaseLibroDigital?Antes de ejecutar, prediga las tres salidas. Después cambie el orden de las clases base de
Registroy vuelva a probar.class ConMarcaDeTiempo: def descripcion(self): return "con fecha" class ConAutor: def descripcion(self): return "con autor" class Registro(ConMarcaDeTiempo, ConAutor): pass r = Registro() print(r.descripcion()) print(Registro.mro()) print(isinstance(r, ConAutor))¿Qué regla usa Python para escoger el método?