Te damos la bienvenida a Colab

Si ya conoces Colab, mira este video para aprender sobre las tablas interactivas, la vista histórica de código ejecutado y la paleta de comandos.

Miniatura de un video que muestra 3 funciones notables de Colab de Google

Función útil para ver los métodos de un objeto#

dir(variable)

retorna una lista con los métodos que contiene un objeto

variable = "Cadena"

print(dir(variable))

# Recordemos que python está construido basado en objetos, por lo que los tipos de variables son también objetos.
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

Argumentos flexibles de una función#

En Python, *args y **kwargs son utilizados para pasar argumentos a una función de una manera más flexible.

*args permite que una función reciba un número variable de argumentos no clave (no nombrados), que serán tratados como una tupla.

**kwargs permite que una función reciba un número variable de argumentos clave (nombrados), que serán tratados como un diccionario.

Aquí está un ejemplo de cómo usar *args y **kwargs en una función en Python:

def my_function(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)
my_function(1, 2, 3, name="John", age=25)

# Output:
# Positional arguments: (1, 2, 3)
# Keyword arguments: {'name': 'John', 'age': 25}
Positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'John', 'age': 25}

En este ejemplo, la función my_function acepta cualquier número de argumentos posicionales y clave. Los argumentos posicionales se pasan a la función usando *args, mientras que los argumentos clave se pasan usando **kwargs. Al llamar a la función, podemos pasar cualquier número de argumentos posicionales y clave, que serán impresos en pantalla.

Recordemos que objeto y clase son similares, ¡pero no lo mismo!:#

Clase y objeto son términos que se utilizan en programación orientada a objetos (OOP, por sus siglas en inglés). En este paradigma, una clase es una plantilla para crear objetos que comparten una estructura de datos y un comportamiento común.

Por otro lado, un objeto es una instancia de una clase, es decir, es una representación concreta de una clase con valores específicos para sus atributos y métodos. Cada objeto puede tener valores distintos para los atributos y comportarse de manera diferente según las acciones que se le apliquen.

Definición de clases#

#Definimos la clase
class Vehicle:
    # definimos el constructor de la clase
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
    # definimos los métodos propios de la clase
    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name
# Instanciamos la clase y le pasamos los atributos definidos en el constructor
my_car = Vehicle("bmw", "x5", 2022) # creamos my_car que es un objeto de tipo Vehículo
print(my_car.get_descriptive_name())
2022 bmw x5

Herencia de clases#

# Vamos a crear una clase carro que hereda de la clase vehículo
class Car(Vehicle):
    def __init__(self, make, model, year, num_doors):
        super().__init__(make, model, year)
        self.num_doors = num_doors
    #Métodos de la clase -- metodo sobrecargado para poder imprimir el numero de puertas
    #def get_descriptive_name(self):
     #   long_name = f"{self.year} {self.make} {self.model} {self.num_doors}"
      #  return long_name.title()

my_car = Car("bmw", "x5", 2022, 4)
print(my_car.get_descriptive_name())
2022 bmw x5
my_car.num_doors
4

Polimorfismo#

#Definimos una clase Padre
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print ("Haciendo sonido de animal")
#Creamos una clase perro que hereda los métodos de Animal.
class Dog(Animal):
    #Se modifica el método speak
    def speak(self):
        print( "Woof!")
class Cat(Animal):
    def speak(self):
        print( "Meow!")
# Instanciamos la clase perro

dog = Dog("Fido")
cat = Cat("Fluffy")

dog.speak()
cat.speak()
Woof!
Meow!

Decoradores#

Un decorador en Python es un tipo especial de función que se utiliza para modificar el comportamiento de otra función. Los decoradores le permiten envolver una función en otra función y agregar funcionalidad adicional a ella.

def bold(func):
    def wrapper():
        return "<b>" + func() + "</b>"
    return wrapper

def italic(func):
    def wrapper():
        return "<i>" + func() + "</i>"
    return wrapper
@bold
@italic
def hello():
    return "Hello, World!"

print(hello())
# Salida:
# <b><i>Hello, World!</i></b>
<b><i>Hello, World!</i></b>

En este ejemplo, tenemos dos decoradores: bold e italic. Cada decorador es una función que toma otra función como argumento y devuelve una nueva función (la función wrapper) que agrega comportamiento adicional a la función original.

La función hello está decorada con ambos decoradores bold e italic mediante el uso del símbolo @. Esto es una forma abreviada de decir:

*hello = bold(italic(hello()))*.

Cuando se llama a la función hello, primero pasa por el decorador italic, que la envuelve en la función wrapper definida en el decorador italic. Esto a su vez devuelve una nueva función que luego se pasa al decorador bold, que la envuelve en su propia función wrapper. El resultado es una nueva función que agrega tanto etiquetas negritas como cursivas al texto devuelto por la función original hello.

En este ejemplo, los decoradores nos permiten agregar fácilmente el mismo comportamiento (etiquetas negritas e cursivas) a múltiples funciones sin tener que modificar las funciones mismas. Esto hace que nuestro código sea más reutilizable y más fácil de mantener.

Clases abstractas#

En Python, no existe una sintaxis explícita para definir interfaces, pero se puede lograr una funcionalidad similar usando clases base abstractas. Una clase base abstracta es una clase que define una interfaz común para sus subclases, pero no se puede instanciar a sí misma.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * (self.radius ** 2)

    def perimeter(self):
        return 2 * 3.14 * self.radius
rectangle=Rectangle(3,4)
rectangle.area()
12
rectangle.perimeter()
14

Consumo de API’s usando la librería Requests#

!pip install requests
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/
Requirement already satisfied: requests in /usr/local/lib/python3.8/dist-packages (2.25.1)
Requirement already satisfied: chardet<5,>=3.0.2 in /usr/local/lib/python3.8/dist-packages (from requests) (4.0.0)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.8/dist-packages (from requests) (2.10)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.8/dist-packages (from requests) (1.24.3)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.8/dist-packages (from requests) (2022.12.7)
import requests
# Consumo de API's
response = requests.get("https://pokeapi.co/api/v2/pokemon?limit=151")
print(response)
print(response.status_code)
print(response.json())
<Response [200]>
200
{'count': 1279, 'next': 'https://pokeapi.co/api/v2/pokemon?offset=151&limit=151', 'previous': None, 'results': [{'name': 'bulbasaur', 'url': 'https://pokeapi.co/api/v2/pokemon/1/'}, {'name': 'ivysaur', 'url': 'https://pokeapi.co/api/v2/pokemon/2/'}, {'name': 'venusaur', 'url': 'https://pokeapi.co/api/v2/pokemon/3/'}, {'name': 'charmander', 'url': 'https://pokeapi.co/api/v2/pokemon/4/'}, {'name': 'charmeleon', 'url': 'https://pokeapi.co/api/v2/pokemon/5/'}, {'name': 'charizard', 'url': 'https://pokeapi.co/api/v2/pokemon/6/'}, {'name': 'squirtle', 'url': 'https://pokeapi.co/api/v2/pokemon/7/'}, {'name': 'wartortle', 'url': 'https://pokeapi.co/api/v2/pokemon/8/'}, {'name': 'blastoise', 'url': 'https://pokeapi.co/api/v2/pokemon/9/'}, {'name': 'caterpie', 'url': 'https://pokeapi.co/api/v2/pokemon/10/'}, {'name': 'metapod', 'url': 'https://pokeapi.co/api/v2/pokemon/11/'}, {'name': 'butterfree', 'url': 'https://pokeapi.co/api/v2/pokemon/12/'}, {'name': 'weedle', 'url': 'https://pokeapi.co/api/v2/pokemon/13/'}, {'name': 'kakuna', 'url': 'https://pokeapi.co/api/v2/pokemon/14/'}, {'name': 'beedrill', 'url': 'https://pokeapi.co/api/v2/pokemon/15/'}, {'name': 'pidgey', 'url': 'https://pokeapi.co/api/v2/pokemon/16/'}, {'name': 'pidgeotto', 'url': 'https://pokeapi.co/api/v2/pokemon/17/'}, {'name': 'pidgeot', 'url': 'https://pokeapi.co/api/v2/pokemon/18/'}, {'name': 'rattata', 'url': 'https://pokeapi.co/api/v2/pokemon/19/'}, {'name': 'raticate', 'url': 'https://pokeapi.co/api/v2/pokemon/20/'}, {'name': 'spearow', 'url': 'https://pokeapi.co/api/v2/pokemon/21/'}, {'name': 'fearow', 'url': 'https://pokeapi.co/api/v2/pokemon/22/'}, {'name': 'ekans', 'url': 'https://pokeapi.co/api/v2/pokemon/23/'}, {'name': 'arbok', 'url': 'https://pokeapi.co/api/v2/pokemon/24/'}, {'name': 'pikachu', 'url': 'https://pokeapi.co/api/v2/pokemon/25/'}, {'name': 'raichu', 'url': 'https://pokeapi.co/api/v2/pokemon/26/'}, {'name': 'sandshrew', 'url': 'https://pokeapi.co/api/v2/pokemon/27/'}, {'name': 'sandslash', 'url': 'https://pokeapi.co/api/v2/pokemon/28/'}, {'name': 'nidoran-f', 'url': 'https://pokeapi.co/api/v2/pokemon/29/'}, {'name': 'nidorina', 'url': 'https://pokeapi.co/api/v2/pokemon/30/'}, {'name': 'nidoqueen', 'url': 'https://pokeapi.co/api/v2/pokemon/31/'}, {'name': 'nidoran-m', 'url': 'https://pokeapi.co/api/v2/pokemon/32/'}, {'name': 'nidorino', 'url': 'https://pokeapi.co/api/v2/pokemon/33/'}, {'name': 'nidoking', 'url': 'https://pokeapi.co/api/v2/pokemon/34/'}, {'name': 'clefairy', 'url': 'https://pokeapi.co/api/v2/pokemon/35/'}, {'name': 'clefable', 'url': 'https://pokeapi.co/api/v2/pokemon/36/'}, {'name': 'vulpix', 'url': 'https://pokeapi.co/api/v2/pokemon/37/'}, {'name': 'ninetales', 'url': 'https://pokeapi.co/api/v2/pokemon/38/'}, {'name': 'jigglypuff', 'url': 'https://pokeapi.co/api/v2/pokemon/39/'}, {'name': 'wigglytuff', 'url': 'https://pokeapi.co/api/v2/pokemon/40/'}, {'name': 'zubat', 'url': 'https://pokeapi.co/api/v2/pokemon/41/'}, {'name': 'golbat', 'url': 'https://pokeapi.co/api/v2/pokemon/42/'}, {'name': 'oddish', 'url': 'https://pokeapi.co/api/v2/pokemon/43/'}, {'name': 'gloom', 'url': 'https://pokeapi.co/api/v2/pokemon/44/'}, {'name': 'vileplume', 'url': 'https://pokeapi.co/api/v2/pokemon/45/'}, {'name': 'paras', 'url': 'https://pokeapi.co/api/v2/pokemon/46/'}, {'name': 'parasect', 'url': 'https://pokeapi.co/api/v2/pokemon/47/'}, {'name': 'venonat', 'url': 'https://pokeapi.co/api/v2/pokemon/48/'}, {'name': 'venomoth', 'url': 'https://pokeapi.co/api/v2/pokemon/49/'}, {'name': 'diglett', 'url': 'https://pokeapi.co/api/v2/pokemon/50/'}, {'name': 'dugtrio', 'url': 'https://pokeapi.co/api/v2/pokemon/51/'}, {'name': 'meowth', 'url': 'https://pokeapi.co/api/v2/pokemon/52/'}, {'name': 'persian', 'url': 'https://pokeapi.co/api/v2/pokemon/53/'}, {'name': 'psyduck', 'url': 'https://pokeapi.co/api/v2/pokemon/54/'}, {'name': 'golduck', 'url': 'https://pokeapi.co/api/v2/pokemon/55/'}, {'name': 'mankey', 'url': 'https://pokeapi.co/api/v2/pokemon/56/'}, {'name': 'primeape', 'url': 'https://pokeapi.co/api/v2/pokemon/57/'}, {'name': 'growlithe', 'url': 'https://pokeapi.co/api/v2/pokemon/58/'}, {'name': 'arcanine', 'url': 'https://pokeapi.co/api/v2/pokemon/59/'}, {'name': 'poliwag', 'url': 'https://pokeapi.co/api/v2/pokemon/60/'}, {'name': 'poliwhirl', 'url': 'https://pokeapi.co/api/v2/pokemon/61/'}, {'name': 'poliwrath', 'url': 'https://pokeapi.co/api/v2/pokemon/62/'}, {'name': 'abra', 'url': 'https://pokeapi.co/api/v2/pokemon/63/'}, {'name': 'kadabra', 'url': 'https://pokeapi.co/api/v2/pokemon/64/'}, {'name': 'alakazam', 'url': 'https://pokeapi.co/api/v2/pokemon/65/'}, {'name': 'machop', 'url': 'https://pokeapi.co/api/v2/pokemon/66/'}, {'name': 'machoke', 'url': 'https://pokeapi.co/api/v2/pokemon/67/'}, {'name': 'machamp', 'url': 'https://pokeapi.co/api/v2/pokemon/68/'}, {'name': 'bellsprout', 'url': 'https://pokeapi.co/api/v2/pokemon/69/'}, {'name': 'weepinbell', 'url': 'https://pokeapi.co/api/v2/pokemon/70/'}, {'name': 'victreebel', 'url': 'https://pokeapi.co/api/v2/pokemon/71/'}, {'name': 'tentacool', 'url': 'https://pokeapi.co/api/v2/pokemon/72/'}, {'name': 'tentacruel', 'url': 'https://pokeapi.co/api/v2/pokemon/73/'}, {'name': 'geodude', 'url': 'https://pokeapi.co/api/v2/pokemon/74/'}, {'name': 'graveler', 'url': 'https://pokeapi.co/api/v2/pokemon/75/'}, {'name': 'golem', 'url': 'https://pokeapi.co/api/v2/pokemon/76/'}, {'name': 'ponyta', 'url': 'https://pokeapi.co/api/v2/pokemon/77/'}, {'name': 'rapidash', 'url': 'https://pokeapi.co/api/v2/pokemon/78/'}, {'name': 'slowpoke', 'url': 'https://pokeapi.co/api/v2/pokemon/79/'}, {'name': 'slowbro', 'url': 'https://pokeapi.co/api/v2/pokemon/80/'}, {'name': 'magnemite', 'url': 'https://pokeapi.co/api/v2/pokemon/81/'}, {'name': 'magneton', 'url': 'https://pokeapi.co/api/v2/pokemon/82/'}, {'name': 'farfetchd', 'url': 'https://pokeapi.co/api/v2/pokemon/83/'}, {'name': 'doduo', 'url': 'https://pokeapi.co/api/v2/pokemon/84/'}, {'name': 'dodrio', 'url': 'https://pokeapi.co/api/v2/pokemon/85/'}, {'name': 'seel', 'url': 'https://pokeapi.co/api/v2/pokemon/86/'}, {'name': 'dewgong', 'url': 'https://pokeapi.co/api/v2/pokemon/87/'}, {'name': 'grimer', 'url': 'https://pokeapi.co/api/v2/pokemon/88/'}, {'name': 'muk', 'url': 'https://pokeapi.co/api/v2/pokemon/89/'}, {'name': 'shellder', 'url': 'https://pokeapi.co/api/v2/pokemon/90/'}, {'name': 'cloyster', 'url': 'https://pokeapi.co/api/v2/pokemon/91/'}, {'name': 'gastly', 'url': 'https://pokeapi.co/api/v2/pokemon/92/'}, {'name': 'haunter', 'url': 'https://pokeapi.co/api/v2/pokemon/93/'}, {'name': 'gengar', 'url': 'https://pokeapi.co/api/v2/pokemon/94/'}, {'name': 'onix', 'url': 'https://pokeapi.co/api/v2/pokemon/95/'}, {'name': 'drowzee', 'url': 'https://pokeapi.co/api/v2/pokemon/96/'}, {'name': 'hypno', 'url': 'https://pokeapi.co/api/v2/pokemon/97/'}, {'name': 'krabby', 'url': 'https://pokeapi.co/api/v2/pokemon/98/'}, {'name': 'kingler', 'url': 'https://pokeapi.co/api/v2/pokemon/99/'}, {'name': 'voltorb', 'url': 'https://pokeapi.co/api/v2/pokemon/100/'}, {'name': 'electrode', 'url': 'https://pokeapi.co/api/v2/pokemon/101/'}, {'name': 'exeggcute', 'url': 'https://pokeapi.co/api/v2/pokemon/102/'}, {'name': 'exeggutor', 'url': 'https://pokeapi.co/api/v2/pokemon/103/'}, {'name': 'cubone', 'url': 'https://pokeapi.co/api/v2/pokemon/104/'}, {'name': 'marowak', 'url': 'https://pokeapi.co/api/v2/pokemon/105/'}, {'name': 'hitmonlee', 'url': 'https://pokeapi.co/api/v2/pokemon/106/'}, {'name': 'hitmonchan', 'url': 'https://pokeapi.co/api/v2/pokemon/107/'}, {'name': 'lickitung', 'url': 'https://pokeapi.co/api/v2/pokemon/108/'}, {'name': 'koffing', 'url': 'https://pokeapi.co/api/v2/pokemon/109/'}, {'name': 'weezing', 'url': 'https://pokeapi.co/api/v2/pokemon/110/'}, {'name': 'rhyhorn', 'url': 'https://pokeapi.co/api/v2/pokemon/111/'}, {'name': 'rhydon', 'url': 'https://pokeapi.co/api/v2/pokemon/112/'}, {'name': 'chansey', 'url': 'https://pokeapi.co/api/v2/pokemon/113/'}, {'name': 'tangela', 'url': 'https://pokeapi.co/api/v2/pokemon/114/'}, {'name': 'kangaskhan', 'url': 'https://pokeapi.co/api/v2/pokemon/115/'}, {'name': 'horsea', 'url': 'https://pokeapi.co/api/v2/pokemon/116/'}, {'name': 'seadra', 'url': 'https://pokeapi.co/api/v2/pokemon/117/'}, {'name': 'goldeen', 'url': 'https://pokeapi.co/api/v2/pokemon/118/'}, {'name': 'seaking', 'url': 'https://pokeapi.co/api/v2/pokemon/119/'}, {'name': 'staryu', 'url': 'https://pokeapi.co/api/v2/pokemon/120/'}, {'name': 'starmie', 'url': 'https://pokeapi.co/api/v2/pokemon/121/'}, {'name': 'mr-mime', 'url': 'https://pokeapi.co/api/v2/pokemon/122/'}, {'name': 'scyther', 'url': 'https://pokeapi.co/api/v2/pokemon/123/'}, {'name': 'jynx', 'url': 'https://pokeapi.co/api/v2/pokemon/124/'}, {'name': 'electabuzz', 'url': 'https://pokeapi.co/api/v2/pokemon/125/'}, {'name': 'magmar', 'url': 'https://pokeapi.co/api/v2/pokemon/126/'}, {'name': 'pinsir', 'url': 'https://pokeapi.co/api/v2/pokemon/127/'}, {'name': 'tauros', 'url': 'https://pokeapi.co/api/v2/pokemon/128/'}, {'name': 'magikarp', 'url': 'https://pokeapi.co/api/v2/pokemon/129/'}, {'name': 'gyarados', 'url': 'https://pokeapi.co/api/v2/pokemon/130/'}, {'name': 'lapras', 'url': 'https://pokeapi.co/api/v2/pokemon/131/'}, {'name': 'ditto', 'url': 'https://pokeapi.co/api/v2/pokemon/132/'}, {'name': 'eevee', 'url': 'https://pokeapi.co/api/v2/pokemon/133/'}, {'name': 'vaporeon', 'url': 'https://pokeapi.co/api/v2/pokemon/134/'}, {'name': 'jolteon', 'url': 'https://pokeapi.co/api/v2/pokemon/135/'}, {'name': 'flareon', 'url': 'https://pokeapi.co/api/v2/pokemon/136/'}, {'name': 'porygon', 'url': 'https://pokeapi.co/api/v2/pokemon/137/'}, {'name': 'omanyte', 'url': 'https://pokeapi.co/api/v2/pokemon/138/'}, {'name': 'omastar', 'url': 'https://pokeapi.co/api/v2/pokemon/139/'}, {'name': 'kabuto', 'url': 'https://pokeapi.co/api/v2/pokemon/140/'}, {'name': 'kabutops', 'url': 'https://pokeapi.co/api/v2/pokemon/141/'}, {'name': 'aerodactyl', 'url': 'https://pokeapi.co/api/v2/pokemon/142/'}, {'name': 'snorlax', 'url': 'https://pokeapi.co/api/v2/pokemon/143/'}, {'name': 'articuno', 'url': 'https://pokeapi.co/api/v2/pokemon/144/'}, {'name': 'zapdos', 'url': 'https://pokeapi.co/api/v2/pokemon/145/'}, {'name': 'moltres', 'url': 'https://pokeapi.co/api/v2/pokemon/146/'}, {'name': 'dratini', 'url': 'https://pokeapi.co/api/v2/pokemon/147/'}, {'name': 'dragonair', 'url': 'https://pokeapi.co/api/v2/pokemon/148/'}, {'name': 'dragonite', 'url': 'https://pokeapi.co/api/v2/pokemon/149/'}, {'name': 'mewtwo', 'url': 'https://pokeapi.co/api/v2/pokemon/150/'}, {'name': 'mew', 'url': 'https://pokeapi.co/api/v2/pokemon/151/'}]}

Su misión…#

Imagina que estás creando un juego de carreras. Para ello, debes modelar diferentes tipos de vehículos, como coches, motos, camiones, etc. Cada tipo de vehículo tiene diferentes atributos y métodos.

Utiliza herencia y polimorfismo para modelar diferentes tipos de vehículos. Además, utiliza decoradores para agregar características especiales a ciertos tipos de vehículos.

Finalmente, crea un programa que utilice estas clases y métodos para simular una carrera entre diferentes tipos de vehículos. Puedes agregar un sistema de puntuación y un cronómetro para hacerlo más interesante.

Ayuda:

  1. Crea una clase Vehiculo que tenga los atributos básicos de un vehículo, como la velocidad máxima y la capacidad del tanque de gasolina. También debe tener métodos para acelerar, frenar

  2. Crea subclases de Vehiculo para cada tipo de vehículo que quieras modelar

  3. Utiliza polimorfismo para implementar métodos específicos de cada subclase.

  4. Utiliza decoradores para agregar características especiales a ciertos tipos de vehículos. Por ejemplo, puedes agregar un decorador llamado Turbo a la subclase Coche que aumente temporalmente la velocidad máxima del coche

  5. Debes crear un programa que utilice estas clases y métodos para simular una carrera entre diferentes tipos de vehículos. Puedes agregar un sistema de puntuación y un cronómetro para hacerlo más interesante.