Clase 5

Temas

  • Decorators
  • Más sobre orientación a objetos.
    • Métodos y atributos de clase
    • Clases abstractas
  • Enumerators
  • TDD: diseño guiado por pruebas.
  • ¡Práctica!

Repaso: Ciudadanos de primera clase

  • Las funciones en Python son ciudadanos de primera
    • Pueden ser tratadas como cualquier tipo.
    • Pueden ser asignadas a variables.
    • Pueden ser pasadas como parámetro a otras funciones.
    • Pueden ser devueltas por otras funciones.

Funciones de primera clase

  • Recibir funciones
In [ ]:
def foo(bar):
    return bar + 1

def call_fun_with_arg(fun, arg):
    return fun(arg)

call_fun_with_arg(foo, 3)

Funciones de primera clase

  • Crear funciones dentro de funciones
In [3]:
def parent():
    print("Printing from the parent() function.")

    def first_child():
        return "Printing from the first_child() function."

    def second_child():
        return "Printing from the second_child() function."

    print(first_child())
    print(second_child())
    
parent()
#Llamar a funciones definidas internamente falla
#first_child()
Printing from the parent() function.
Printing from the first_child() function.
Printing from the second_child() function.

Nota: Estas funciones se llaman Nested Functions, en standard C no se permiten este tipo de funciones (aunque el compilador gcc agrega esta extensión de lenguaje)

Funciones de primera clase

  • Devolver nested-functions
In [6]:
def parent(num):

    def first_child():
        return "Printing from the first_child() function."

    def second_child():
        return "Printing from the second_child() function."

    if num == 10:
        return first_child
    else:
        return second_child
    
ret_fun = parent(10)
print ret_fun()
Printing from the first_child() function.

Decorators

  • Definición: Función que toma una función y extiende el comportamiento de la función sin modificarla explicitamente.

  • Esto es posible debido a que las funciones son ciudadanas de primera clase (first-class citizen).

Nota: Existe un patrón de diseño en POO que se llama Decorator Pattern y permite agregar comportamiento a un objeto.

 Ejemplo de Decorator

In [8]:
def my_decorator(some_function):

    def wrapper():
        print("Something is happening before some_function() is called.")
        some_function()
        print("Something is happening after some_function() is called.")

    return wrapper

def just_some_function():
    print("Wheee!")

just_some_function = my_decorator(just_some_function)
 
just_some_function()
Something is happening before some_function() is called.
Wheee!
Something is happening after some_function() is called.

Sintaxis de Decorator

In [19]:
def my_decorator(some_function):

    def wrapper(*args):
        print("Something is happening before some_function() is called.")
        some_function(*args)
        print("Something is happening after some_function() is called.")

    return wrapper


@my_decorator
def just_some_function(n):
    print("Wheee! " + str(n))

just_some_function(10)
Something is happening before some_function() is called.
Wheee! 10
Something is happening after some_function() is called.

 Sintaxis de Decorator

@decorator def funcion():

Equivale a:

funcion = decorator(funcion)

Volviendo a la Clase pasada, veamos como funciona property...

jmp Clase 4

Ejercicio 5.1

Escribir un decorator timing_function que permita agregar una impresión del tiempo en que tarda en correr una función.

Usar el método time de la librería time().

from time import time
time()

Solución 5.1

In [22]:
from time import time

def timing_function(some_function):

    """
    Outputs the time a function takes
    to execute.
    """

    def wrapper():
        t1 = time()
        some_function()
        t2 = time()
        return "Time it took to run the function: " + str((t2 - t1))
    return wrapper


@timing_function
def my_function():
    num_list = []
    for num in (range(0, 10000000)):
        num_list.append(num)
    print("\nSum of all the numbers: " + str((sum(num_list))))
    
my_function()
Sum of all the numbers: 49999995000000
Out[22]:
'Time it took to run the function: 1.01930785179'
In [47]:
class ClaseDePrueba(object):
    atributo_de_clase = 0
    def __init__(self, atributo_de_instancia):
        self.atributo_de_instancia = atributo_de_instancia
In [48]:
# Atributo de clase se busca en ClaseDePrueba.__dict__, se encuentra.
print ClaseDePrueba.atributo_de_clase
0
In [49]:
instancia_de_prueba = ClaseDePrueba(9)
# Atributo de instancia se busca en instancia_de_prueba.__dict__, se encuentra.
print instancia_de_prueba.atributo_de_instancia 
9
In [50]:
# Atributo de clase se busca en instancia_de_prueba.__dict__, no se encuentra;
# entonces se busca en ClaseDePrueba.__dict__
print instancia_de_prueba.atributo_de_clase 
0
In [51]:
instancia_de_prueba.atributo_de_clase = 1
# Atributo de clase se busca en instancia_de_prueba.__dict__, se encuentra.
print instancia_de_prueba.atributo_de_clase
1
In [52]:
print ClaseDePrueba.atributo_de_clase
0
In [53]:
ClaseDePrueba.atributo_de_clase = 123
print instancia_de_prueba.atributo_de_clase
1
In [54]:
instancia_de_prueba2 = ClaseDePrueba(0)
print instancia_de_prueba2.atributo_de_clase
123

Métodos de instancia, de clase y estáticos

In [55]:
class ClaseDePrueba(object):
    
    def metodo_de_instancia(self):
        print "En metodo de instancia"
    
    @classmethod
    def metodo_de_clase(cls):
        print "En metodo de clase"
        
    @staticmethod
    def metodo_de_clase_estatico():
        print "En metodo de clase estatico"
In [58]:
# Rompe!        
#ClaseDePrueba.metodo_de_instancia()
ClaseDePrueba.metodo_de_clase()
ClaseDePrueba.metodo_de_clase_estatico()

instancia = ClaseDePrueba()
instancia.metodo_de_instancia()
instancia.metodo_de_clase()
instancia.metodo_de_clase_estatico()
En metodo de clase
En metodo de clase estatico
En metodo de instancia
En metodo de clase
En metodo de clase estatico

@staticmethod vs @classmethod

  • Ambos tipos de métodos se identifican con la clase, no con una instancia de la misma.
  • No requieren que se cree una instancia, por lo que no dependen del estado del objeto.
  • Se diferencian en que:
    • Los métodos estáticos no tienen ningun conocimiento de la clase, solo manejan los parámetros. Es como una función encapsulada en la clase.
    • Los métodos de clase trabajan con la clase, dado que siempre la recibe por parámetro.

Clases abstractas

  • Clases con métodos no implementados que no se pueden instanciar.

Hasta ahora podemos simular el comportamiento de clases abstractas:

class BaseAbstracta:
    def metodo_heredado(self):
        print("Método heredado en Base.")

    def metodo_sin_parametros(self):
        raise NotImplementedError

    @staticmethod
    def metodo_estatico():
        raise NotImplementedError

¿Qué desventaja tiene?

¡Si falta implementar algún método nos enteramos en tiempo de ejecución!

¿Cómo lo solucionamos?

Python introduce la AbstractBaseClass que podemos importar del módulo abc

In [59]:
import abc
help(abc)
Help on module abc:

NAME
    abc - Abstract Base Classes (ABCs) according to PEP 3119.

FILE
    /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/abc.py

MODULE DOCS
    https://docs.python.org/library/abc

CLASSES
    __builtin__.property(__builtin__.object)
        abstractproperty
    __builtin__.type(__builtin__.object)
        ABCMeta
    
    class ABCMeta(__builtin__.type)
     |  Metaclass for defining Abstract Base Classes (ABCs).
     |  
     |  Use this metaclass to create an ABC.  An ABC can be subclassed
     |  directly, and then acts as a mix-in class.  You can also register
     |  unrelated concrete classes (even built-in classes) and unrelated
     |  ABCs as 'virtual subclasses' -- these and their descendants will
     |  be considered subclasses of the registering ABC by the built-in
     |  issubclass() function, but the registering ABC won't show up in
     |  their MRO (Method Resolution Order) nor will method
     |  implementations defined by the registering ABC be callable (not
     |  even via super()).
     |  
     |  Method resolution order:
     |      ABCMeta
     |      __builtin__.type
     |      __builtin__.object
     |  
     |  Methods defined here:
     |  
     |  __instancecheck__(cls, instance)
     |      Override for isinstance(instance, cls).
     |  
     |  __subclasscheck__(cls, subclass)
     |      Override for issubclass(subclass, cls).
     |  
     |  register(cls, subclass)
     |      Register a virtual subclass of an ABC.
     |  
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |  
     |  __new__(mcls, name, bases, namespace)
     |  
     |  ----------------------------------------------------------------------
     |  Methods inherited from __builtin__.type:
     |  
     |  __call__(...)
     |      x.__call__(...) <==> x(...)
     |  
     |  __delattr__(...)
     |      x.__delattr__('name') <==> del x.name
     |  
     |  __eq__(...)
     |      x.__eq__(y) <==> x==y
     |  
     |  __ge__(...)
     |      x.__ge__(y) <==> x>=y
     |  
     |  __getattribute__(...)
     |      x.__getattribute__('name') <==> x.name
     |  
     |  __gt__(...)
     |      x.__gt__(y) <==> x>y
     |  
     |  __hash__(...)
     |      x.__hash__() <==> hash(x)
     |  
     |  __init__(...)
     |      x.__init__(...) initializes x; see help(type(x)) for signature
     |  
     |  __le__(...)
     |      x.__le__(y) <==> x<=y
     |  
     |  __lt__(...)
     |      x.__lt__(y) <==> x<y
     |  
     |  __ne__(...)
     |      x.__ne__(y) <==> x!=y
     |  
     |  __repr__(...)
     |      x.__repr__() <==> repr(x)
     |  
     |  __setattr__(...)
     |      x.__setattr__('name', value) <==> x.name = value
     |  
     |  __subclasses__(...)
     |      __subclasses__() -> list of immediate subclasses
     |  
     |  mro(...)
     |      mro() -> list
     |      return a type's method resolution order
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from __builtin__.type:
     |  
     |  __abstractmethods__
     |  
     |  __base__
     |  
     |  __bases__
     |  
     |  __basicsize__
     |  
     |  __dict__
     |  
     |  __dictoffset__
     |  
     |  __flags__
     |  
     |  __itemsize__
     |  
     |  __mro__
     |  
     |  __weakrefoffset__
    
    class abstractproperty(__builtin__.property)
     |  A decorator indicating abstract properties.
     |  
     |  Requires that the metaclass is ABCMeta or derived from it.  A
     |  class that has a metaclass derived from ABCMeta cannot be
     |  instantiated unless all of its abstract properties are overridden.
     |  The abstract properties can be called using any of the normal
     |  'super' call mechanisms.
     |  
     |  Usage:
     |  
     |      class C:
     |          __metaclass__ = ABCMeta
     |          @abstractproperty
     |          def my_abstract_property(self):
     |              ...
     |  
     |  This defines a read-only property; you can also define a read-write
     |  abstract property using the 'long' form of property declaration:
     |  
     |      class C:
     |          __metaclass__ = ABCMeta
     |          def getx(self): ...
     |          def setx(self, value): ...
     |          x = abstractproperty(getx, setx)
     |  
     |  Method resolution order:
     |      abstractproperty
     |      __builtin__.property
     |      __builtin__.object
     |  
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  __isabstractmethod__ = True
     |  
     |  ----------------------------------------------------------------------
     |  Methods inherited from __builtin__.property:
     |  
     |  __delete__(...)
     |      descr.__delete__(obj)
     |  
     |  __get__(...)
     |      descr.__get__(obj[, type]) -> value
     |  
     |  __getattribute__(...)
     |      x.__getattribute__('name') <==> x.name
     |  
     |  __init__(...)
     |      x.__init__(...) initializes x; see help(type(x)) for signature
     |  
     |  __set__(...)
     |      descr.__set__(obj, value)
     |  
     |  deleter(...)
     |      Descriptor to change the deleter on a property.
     |  
     |  getter(...)
     |      Descriptor to change the getter on a property.
     |  
     |  setter(...)
     |      Descriptor to change the setter on a property.
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from __builtin__.property:
     |  
     |  fdel
     |  
     |  fget
     |  
     |  fset
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes inherited from __builtin__.property:
     |  
     |  __new__ = <built-in method __new__ of type object>
     |      T.__new__(S, ...) -> a new object with type S, a subtype of T

FUNCTIONS
    abstractmethod(funcobj)
        A decorator indicating abstract methods.
        
        Requires that the metaclass is ABCMeta or derived from it.  A
        class that has a metaclass derived from ABCMeta cannot be
        instantiated unless all of its abstract methods are overridden.
        The abstract methods can be called using any of the normal
        'super' call mechanisms.
        
        Usage:
        
            class C:
                __metaclass__ = ABCMeta
                @abstractmethod
                def my_abstract_method(self, ...):
                    ...


from abc import ABCMeta, abstractmethod

class BaseAbstracta:
    __metaclass__  = ABCMeta

    def metodo_heredado(self):
        print("Método heredado en Base.")

    @abstractmethod
    def metodo_sin_parametros(self):
        print("Método sin parámetros en Base.")

    @staticmethod
    @abstractmethod
    def metodo_estatico():
        print("Método estático en Base.")

Una Comparación

In [ ]:
class BaseAbstracta:

    def metodo_heredado(self):
        print("Método heredado en Base.")

    def metodo_sin_parametros(self):
        raise NotImplementedError
    
    @staticmethod
    def metodo_estatico():
        raise NotImplementedError

    @property
    def atributo(self):
        raise NotImplementedError

    @atributo.setter
    def atributo(self, valor):
        raise NotImplementedError
    
In [ ]:
class DerivadaConcreta(BaseAbstracta):

    @staticmethod
    def metodo_estatico():
        print("Método estático en Derivada.")

    @property
    def atributo(self):
        print("Property getter en Derivada.")

    @atributo.setter
    def atributo(self, valor):
        print("Property setter en Derivada:", str(valor))
In [ ]:
from abc import ABCMeta, abstractmethod

class BaseAbstracta():
    
    __metaclass__  = ABCMeta

    def metodo_heredado(self):
        print("Método heredado en Base.")

    @abstractmethod
    def metodo_sin_parametros(self):
        print("Método sin parámetros en Base.")
    
    @staticmethod
    @abstractmethod
    def metodo_estatico():
        print("Método estático en Base.")

    @property
    @abstractmethod
    def atributo(self):
        print("Property getter en Base.")

    @atributo.setter
    @abstractmethod
    def atributo(self, valor):
        print("Property setter en Base:", str(valor))
    
In [ ]:
class DerivadaConcreta(BaseAbstracta):

    def metodo_sin_parametros(self):
        print("Método sin parámetros en Derivada.")

    def metodo_estatico():
        print("Método estático en Derivada.")

    @property
    def atributo(self):
        print("Property getter en Derivada.")

    @atributo.setter
    def atributo(self, valor):
        print("Property setter en Derivada:", str(valor))

Veamos un ejemplo

In [71]:
from abc import ABCMeta, abstractmethod

class Instrumento:
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def tocar(self):
        pass
    
class Guitarra(Instrumento):
    
    def tocar(self):
        print("Tinggg *sonido de guitarra*")

class Bateria(Instrumento):
    
    def tocar(self):
        print("BOM BOM!")
        
class Bajo(Instrumento):
    pass #TODO
In [72]:
guitarra = Guitarra()
bateria = Bateria()

guitarra.tocar()
bateria.tocar()
Tinggg *sonido de guitarra*
BOM BOM!
In [73]:
bajo = Bajo()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-73-e4f34c5488d2> in <module>()
----> 1 bajo = Bajo()

TypeError: Can't instantiate abstract class Bajo with abstract methods tocar

Ejercicio 5.3

Implementar las clases concretas Circulo, Cuadrado y Rectangulo con constructores:

  • Circulo(radio, color)
  • Cuadrado(lado, color)
  • Rectangulo(base, altura, color)

Que deben heredar de la clase abstracta Figura con los métodos abstractos obtener_perimetro y obtener_area, y el getter de la property concreta color.

Solución 5.3

In [74]:
from abc import ABCMeta, abstractmethod
from math import pi

class Figura:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __init__(self):
        pass

    @abstractmethod
    def obtener_perimetro(self):
        pass

    @abstractmethod
    def obtener_area(self):
        pass

    @property
    def color(self):
        return self._color
In [75]:
class Circulo(Figura):

    def __init__(self, radio, color):
        self.radio = radio
        self._color = color

    def obtener_perimetro(self):
        return pi * 2 * self.radio
        
    def obtener_area(self):
        return pi * self.radio ** 2
In [76]:
class Cuadrado(Figura):

    def __init__(self, lado, color):
        self.lado = lado
        self._color = color

    def obtener_perimetro(self):
        return self.lado * 4

    def obtener_area(self):
        return self.lado ** 2
In [77]:
class Rectangulo(Figura):

    def __init__(self, base, altura, color):
        self.base = base
        self.altura = altura
        self._color = color

    def obtener_perimetro(self):
        return self.base * 2 + self.altura * 2

    def obtener_area(self):
        return self.base * self.altura
In [78]:
lista_de_figuras = []
lista_de_figuras.append(Circulo(5,"Rojo"))
lista_de_figuras.append(Cuadrado(10,"Azul"))
lista_de_figuras.append(Rectangulo(5,10,"Amarillo"))

for figura in lista_de_figuras:
    print figura.obtener_perimetro()
    print figura.obtener_area()
31.4159265359
78.5398163397
40
100
30
50

Enumerators

  • Tipos enumerados ("enums"): conjunto de valores constantes, inmutables con algún contenido semántico.
  • Disponible desde Python 3.4 (backport a versiones más viejas)
  • Importar Enum de la biblioteca enum.

Sintaxis

In [91]:
from enum import Enum

class Dia(Enum):
    LUNES = 1
    MARTES = 2
    MIERCOLES = 3
    JUEVES = 4
    VIERNES = 5
    SABADO = 6
    DOMINGO = 7

Uso

In [92]:
Dia.LUNES
Out[92]:
<Dia.LUNES: 1>
In [93]:
type(Dia.LUNES)
Out[93]:
<enum 'Dia'>
In [94]:
Dia.LUNES == Dia.MARTES
Out[94]:
False
In [95]:
Dia.LUNES == 1 #Porque da False? Usar IntEnum
Out[95]:
False
In [99]:
for dia in Dia:
    print(dia.name, dia.value)
('LUNES', 1)
('MARTES', 2)
('MIERCOLES', 3)
('JUEVES', 4)
('VIERNES', 5)
('SABADO', 6)
('DOMINGO', 7)

Pueden almacenar sinónimos

In [101]:
class Sexo(Enum): 
    FEMENINO = 1
    MASCULINO = 2 
    F=1
    M=2
    MUJER = 1 
    HOMBRE = 2
    NORESPONDE = 3
    
print Sexo.MASCULINO == Sexo.M == Sexo.HOMBRE
print Sexo.M == Sexo.F
True
False

 Con valores diferentes

In [105]:
from enum import unique

@unique
class Continente(Enum):
    ASIA = 1
    EUROPA = 2 
    AMERICA = 3 
    #OCEANIA = 3 
    OCEANIA = 4
    AFRICA = 5 
    ANTARTIDA = 6

Con comportamiento

In [115]:
class Planeta(Enum):
    MERCURIO = (3.303e+23, 2.4397e6) 
    VENUS = (4.869e+24, 6.0518e6) 
    TIERRA = (5.976e+24, 6.37814e6) 
    MARTE = (6.421e+23, 3.3972e6) 
    JUPITER = (1.9e+27, 7.1492e7)
    SATURNO = (5.688e+26, 6.0268e7) 
    URANO = (8.686e+25, 2.5559e7) 
    NEPTUNO = (1.024e+26, 2.4746e7) 
    # (Plutón no)
    
    def __init__(self, masa, radio):
        self.masa = masa
        self.radio = radio
        
    @property
    def gravedad_en_superficie(self):
        G = 6.673e-11
        return (G*self.masa)/self.radio**2
    
    def __str__(self):
        return "Hola soy " + self.name
    
Planeta.TIERRA.gravedad_en_superficie
Out[115]:
9.802652743337129
In [112]:
print type(Planeta.TIERRA)
print Planeta.TIERRA.masa
print Planeta.TIERRA
<enum 'Planeta'>
5.976e+24
Hola soy TIERRA

Ejercicio 5.4

Sabiendo que la gravedad en la superficie de un planeta está dada por la fórmula:

`g = G*M/R^2`

siendo G la constante de gravitación universal G = 6,673*10^11, M la masa del planeta y R su radio.

Escribir la property gravedad_en_superficie que determine la gravedad en la superficie de cada Planeta para el tipo enumerado.

Solución 5.4

In [120]:
class Planeta(Enum):
    MERCURIO = (3.303e+23, 2.4397e6) 
    VENUS = (4.869e+24, 6.0518e6) 
    TIERRA = (5.976e+24, 6.37814e6) 
    MARTE = (6.421e+23, 3.3972e6) 
    JUPITER = (1.9e+27, 7.1492e7)
    SATURNO = (5.688e+26, 6.0268e7) 
    URANO = (8.686e+25, 2.5559e7) 
    NEPTUNO = (1.024e+26, 2.4746e7) 
    # (Plutón no)
    
    def __init__(self, masa, radio):
        self.masa = masa
        self.radio = radio
        
    @property
    def gravedad_en_superficie(self):
        # G: constante de gravitación universal 
        G = 6.67300E-11
        return G * self.masa / (self.radio ** 2)
    
print Planeta.TIERRA.gravedad_en_superficie
9.80265274334

Ejercicio 5.5

Escribir el enumerado Palo para modelar los cuatro palos de las cartas de la baraja española, la clase Carta con atributos palo y numero, y la clase Mazo que al inicializarse instancie las cuarenta cartas y las almacene en una lista.

Solución 5.5

In [121]:
from enum import Enum, unique

@unique
class Palo(Enum):
    BASTO = "Basto"
    ORO = "Oro"
    ESPADA = "Espada"
    COPA = "Copa"

    def __init__(self, nombre):
        self.nombre = nombre

    def __str__(self):
        return self.nombre

    def __repr__(self):
        return str(self)
In [123]:
class Carta:
    ''' Clase que modela una carta. '''

    def __init__(self, palo, numero):
        self.palo = palo
        self.numero = numero

    def __str__(self):
        return str(self.numero) + " de " + str(self.palo)

    def __repr__(self):
        return str(self)


class Mazo:
    ''' Clase que modela un mazo de la baraja española. '''

    def __init__(self):
        self.cartas = [ Carta(palo, numero) for palo in Palo for numero in range(1,13) ]

TDD : Test Driven Development

TDD es una práctica de la Ingeniería de Software que involucra TFD y Refactoring

-"This approach allows you to escape the trap that many developers fall into."

Propone

  • Escribir un Test unitario que falle
  • Escribir el código que haga pasar el Test
  • Refactorizar
  • Repetir 🔁

Ventajas

  • Los desarrolladores no nos “auto condicionamos”.
  • Ayuda a entender el problema, ¡es una técnica de diseño!
  • Finaliza el proceso con un conjunto de pruebas automatizadas.
  • Las pruebas sirven como documentación.

Desventajas

  • Requiere mucha disciplina.
  • Se abandona la metodología bajo presión.

No Silver Bullet

TDD requiere que las pruebas puedan automatizarse esto resulta complejo en los casos de:

  • Interfaces Gráficas
  • Objetos distribuidos
  • Bases de datos

Ciclo de TDD

<img src="img/tdd.png" width=50% height=50% />

Calculadora

La empresa bancaria ASIV nos pide implementar una calculadora en Python ya que no poseen el dinero para poder adquirir una para sus empleados de Finanzas.

La calculadora debe soportar las siguientes operaciones (recordando el último valor calculado):

  • Sumar
  • Restar
  • Multiplicar
  • Dividir (flotante)

Debido a que es un Cliente importante debemos tomar el proyecto seriamente ya que si entregamos un producto que falle nos embargan todo. Por lo que el PM nos obliga pide amablemente utilizar TDD

Vamos a practicar!

Ejercicio 5.6

Implementar la clase CuentaBancaria usando TDD, con los métodos depositar_dinero, retirar_dinero, transferir_dinero y obtener_balance.

Ejercicio 5.7

Implementar la clase Pila con los métodos apilar, desapilar, ver_tope y esta_vacia.

Ejercicio 5.8

Implementar la clase Cola con los métodos encolar, desencolar, ver_tope y esta_vacia.

Ejercicio 5.9

Implementar la clase ListaEnlazada, con los métodos append, pop, len, str y un iterador.

Próxima Clase

  • Escritura de scripts.
  • Manejo de archivos.
  • Persistencia de datos: Pickle.