• ¡Obtenga la seguridad de la aplicación de la manera correcta! Detectar, proteger, monitorear, acelerar y más ...
  • En este artículo, creará un  Aplicación de tablas de multiplicar, utilizando el poder de la programación orientada a objetos (OOP) en Python.

    Practicarás los conceptos principales de OOPy cómo usarlos en una aplicación completamente funcional.

    Python es un lenguaje de programación multiparadigma, lo que significa que nosotros como desarrolladores podemos elegir la mejor opción para cada situación y problema. Cuando hablamos de Programación Orientada a Objetos, nos referimos a uno de los paradigmas más utilizados para construir aplicaciones escalables, en las últimas décadas.

    Los fundamentos de la programación orientada a objetos

    Vamos a echar un vistazo rápido al concepto más importante de POO en Python, las clases.

    A clase es una plantilla en la que definimos la estructura y el comportamiento de los objetos. Esa plantilla nos permite crear Instancias, que no son más que objetos individuales hechos siguiendo la composición de la clase.

    Una clase de libro simple, con los atributos de título y color, se definiría de la siguiente manera.

    class Book:
        def __init__(self, title, color):
            self.title = title
            self.color = color

    Si queremos crear instancias del libro de la clase, debemos llamar a la clase y pasar argumentos a la misma.

    # Instance objects of Book class
    blue_book = Book("The blue kid", "Blue")
    green_book = Book("The frog story", "Green")

    Una buena representación de nuestro programa actual sería:

    Class.png

    Lo asombroso es que cuando comprobamos el tipo de libro Azul y libro Verde  instancias, obtenemos "Libro".

    # Printing the type of the books
    
    print(type(blue_book))
    # <class '__main__.Book'>
    print(type(green_book))
    # <class '__main__.Book'>

    Después de tener estos conceptos muy claros, podemos comenzar a construir el proyecto 😃.

    Declaración de proyecto

    Mientras trabaja como desarrolladores / programadores, la mayor parte del tiempo no se dedica a escribir código, según thenewstack solo dedicamos un tercio de nuestro tiempo a escribir o refactorizar código.

    Pasamos los otros dos tercios leyendo el código de otros y analizando el problema estamos trabajando en.

    Entonces, para este proyecto, generaré una declaración de problema y analizaremos cómo crear nuestra aplicación a partir de ella. Como resultado, estamos haciendo el proceso completo, desde pensar en la solución hasta aplicarla con código.

    Una maestra de primaria quiere un juego para probar las habilidades de multiplicación de los estudiantes de 8 a 10 años.

    El juego debe tener un sistema de vidas y puntos, donde el estudiante comienza con 3 vidas y debe alcanzar una cierta cantidad de puntos para ganar. El programa debe mostrar un mensaje de "perder" si el estudiante agota toda su vida.

    El juego debe tener dos modos, multiplicaciones aleatorias y multiplicaciones de tablas.

    El primero debe darle al alumno una multiplicación aleatoria del 1 al 10, y debe responder correctamente para ganar un punto. Si eso no ocurre, el estudiante pierde un live y el juego continúa. El alumno solo gana cuando alcanza los 5 puntos.

    El segundo modo debe mostrar una tabla de multiplicar del 1 al 10, donde el estudiante debe ingresar el resultado de la multiplicación respectiva. Si el alumno falla 3 veces pierde, pero si completa dos mesas, el juego termina.

    Sé que los requisitos pueden ser un poco mayores, pero te prometo que los resolveremos en este artículo 😁.

    Divide y vencerás

    La habilidad más importante en la programación es la resolución de problemas. Esto se debe a que necesita tener un plan antes de comenzar a comenzar. piratear el código.

    Siempre sugiero tomar el problema más grande y dividirlo en otros más pequeños que puedan resolverse de manera fácil y eficiente.

    Entonces, si necesita crear un juego, comience dividiéndolo en las partes más importantes. Estos subproblemas serán mucho más fáciles de resolver.

    En ese momento, puede tener la claridad de cómo ejecutar e integrar todo con el código.

    Así que hagamos un gráfico de cómo se vería el juego.

    divide y conquistar.png

    Este gráfico establece las relaciones entre los objetos de nuestra aplicación. Como puede ver, los dos objetos principales son Multiplicación aleatoria y Multiplicación de tablas. Y lo único que comparten son los atributos Puntos y  Vive.

    Teniendo toda esta información en mente, entremos en el código.

    Creando la clase de juego para padres

    Cuando trabajamos con programación orientada a objetos, buscamos la forma más limpia de evitar la repetición de código. Se llama SECO (no te repitas).

    Nota: Este objetivo no está relacionado con escribir menos líneas de código (la calidad del código no debe medirse por ese aspecto) sino abstraer las más utilizadas lógica.

    Según la idea anterior, la clase padre de nuestra aplicación debe establecer la estructura y el comportamiento deseado de las otras dos clases.

    Veamos cómo se haría.

    class BaseGame:
    
        # Lenght which the message is centered
        message_lenght = 60
        
        description = ""    
            
        def __init__(self, points_to_win, n_lives=3):
            """Base game class
    
            Args:
                points_to_win (int): the points the game will need to be finished 
                n_lives (int): The number of lives the student have. Defaults to 3.
            """
            self.points_to_win = points_to_win
    
            self.points = 0
            
            self.lives = n_lives
    
        def get_numeric_input(self, message=""):
    
            while True:
                # Get the user input
                user_input = input(message) 
                
                # If the input is numeric, return it
                # If it isn't, print a message and repeat
                if user_input.isnumeric():
                    return int(user_input)
                else:
                    print("The input must be a number")
                    continue     
                 
        def print_welcome_message(self):
            print("PYTHON MULTIPLICATION GAME".center(self.message_lenght))
    
        def print_lose_message(self):
            print("SORRY YOU LOST ALL OF YOUR LIVES".center(self.message_lenght))
    
        def print_win_message(self):
            print(f"CONGRATULATION YOU REACHED {self.points}".center(self.message_lenght))
            
        def print_current_lives(self):
            print(f"Currently you have {self.lives} lives\n")
    
        def print_current_score(self):
            print(f"\nYour score is {self.points}")
    
        def print_description(self):
            print("\n\n" + self.description.center(self.message_lenght) + "\n")
    
        # Basic run method
        def run(self):
            self.print_welcome_message()
            
            self.print_description()

    Vaya, esta parece una clase bastante grande. Déjame explicarlo en profundidad.

    Primero que nada, entendamos el atributos de clase y el constructor.

    Constructor-juego-base.png

    Básicamente, atributos de clase, son variables creadas dentro de la clase, pero fuera del constructor o de cualquier método.

    Aunque la atributos de instancia son variables creadas solo dentro del constructor.

    La principal diferencia entre estos dos es el alcance. es decir, los atributos de clase son accesibles tanto desde un objeto de instancia como desde la clase. Por otro lado, los atributos de instancia solo son accesibles desde un objeto de instancia.

    game = BaseGame(5)
    
    # Accessing game message lenght class attr from class
    print(game.message_lenght) # 60
    
    # Accessing the message_lenght class attr from class
    print(BaseGame.message_lenght)  # 60
    
    # Accessing the points instance attr from instance
    print(game.points) # 0
    
    # Accesing the points instance attribute from class
    print(BaseGame.points) # Attribute error
    

    Otro artículo puede profundizar en este tema. Mantente en contacto para leerlo.

    El  get_numeric_input La función se utiliza para evitar que el usuario proporcione una entrada que no sea numérica. Como puede notar, este método está diseñado para preguntar al usuario hasta que obtenga una entrada numérica. Lo usaremos más adelante en las clases del niño.

    Entrada-juego-base.png

    La impresión métodos nos permite guardar la repetición de imprimir lo mismo cada vez que ocurre un evento en el juego.

    Por último, pero no menos importante, el corrida El método es solo una envoltura que el Multiplicación aleatoria y Multiplicación de tablas Las clases utilizarán para interactuar con el usuario y hacer que todo sea funcional.

    Base-game-run.png

    Creando las clases del niño

    Una vez que hemos creado esa clase principal, que establece la estructura y algunas de las funciones de nuestra aplicación, es hora de crear las clases reales del modo de juego, utilizando el poder de herencia.

    Clase de multiplicación aleatoria

    Esta clase ejecutará el "primer modo" de nuestro juego. Va a utilizar el azar módulo por supuesto, que nos dará la posibilidad de pedir al usuario operaciones aleatorias del 1 al 10. Aquí es un excelente artículo sobre los módulos aleatorios (y otros módulos importantes) 😉.

    import random # Module for random operations
    class RandomMultiplication(BaseGame):
    
        description = "In this game you must answer the random multiplication correctly\nYou win if you reach 5 points, or lose if you lose all your lives"
    
        def __init__(self):
            # The numbers of points needed to win are 5
            # Pass 5 "points_to_win" argument
            super().__init__(5)
    
        def get_random_numbers(self):
    
            first_number = random.randint(1, 10)
            second_number = random.randint(1, 10)
    
            return first_number, second_number
            
        def run(self):
            
            # Call the upper class to print the welcome messages
            super().run()
            
    
            while self.lives > 0 and self.points_to_win > self.points:
                # Gets two random numbers
                number1, number2 = self.get_random_numbers()
    
                operation = f"{number1} x {number2}: "
    
                # Asks the user to answer that operation 
                # Prevent value errors
                user_answer = self.get_numeric_input(message=operation)
    
                if user_answer == number1 * number2:
                    print("\nYour answer is correct\n")
                    
                    # Adds a point
                    self.points += 1
                else:
                    print("\nSorry, your answer is incorrect\n")
    
                    # Substracts a live
                    self.lives -= 1
                
                self.print_current_score()
                self.print_current_lives()
                
            # Only get executed when the game is finished
            # And none of the conditions are true
            else:
                # Prints the final message
                
                if self.points >= self.points_to_win:
                    self.print_win_message()
                else:
                    self.print_lose_message()
    

    Aquí hay otra clase masiva 😅. Pero como dije antes, no es el número de líneas que toma, es lo legible y eficiente que es. Y lo mejor de Python es que permite a los desarrolladores crear código limpio y legible como si estuvieran hablando en inglés normal.

    Esta clase tiene una cosa que puede confundirte, pero la explicaré de la manera más simple posible.

        # Parent class
        def __init__(self, points_to_win, n_lives=3):
            "...
        # Child class
        def __init__(self):
            # The numbers of points needed to win are 5
            # Pass 5 "points_to_win" argument
            super().__init__(5)

    El constructor de la clase secundaria está llamando al súper función que, al mismo tiempo, se refiere a la clase principal (BaseGame). Básicamente le dice a Python:

    ¡Complete el atributo "points_to_win" de la clase principal con 5!

    No es necesario poner yo, dentro de  super().__init__() part solo porque estamos llamando a super dentro del constructor, y resultaría en redundante.

    También estamos usando el súper función en el método de ejecución, y veremos qué está sucediendo en ese fragmento de código.

        # Basic run method
        # Parent method
        def run(self):
            self.print_welcome_message()
            
            self.print_description()
        def run(self):
            
            # Call the upper class to print the welcome messages
            super().run()
            
            .....

    Como puede observar el método de ejecución en la clase principal, imprima el mensaje de bienvenida y descripción. Pero es una buena idea mantener esa funcionalidad y también agregar otras adicionales en las clases secundarias. De acuerdo con eso, usamos súper  para ejecutar todo el código del método padre antes de ejecutar la siguiente pieza.

    La otra parte de la función de ejecución es bastante sencilla. Pide al usuario un número con el mensaje de la operación que debe responder. Luego se compara el resultado con la multiplicación real y si son iguales se suma un punto, si no se quitan 1 vida.

    Vale la pena decir que estamos usando bucles while-else. Esto excede el alcance de este artículo, pero publicaré uno al respecto en unos días.

    Finalmente, get_random_numbers, usa la función random.randint, que devuelve un número entero aleatorio dentro del rango especificado. Luego devuelve una tupla de dos enteros aleatorios.

    Clase de multiplicación aleatoria

    El "segundo modo", debe mostrar el juego en formato de tabla de multiplicar, y asegurarse de que el usuario responde correctamente al menos 2 tablas.

    Para ese propósito, usaremos nuevamente el poder de súper y modificar el atributo de la clase padre puntos_para_ganar a 2.

    class TableMultiplication(BaseGame):
    
        description = "In this game you must resolve the complete multiplication table correctly\nYou win if you solve 2 tables"
        
        def __init__(self):
            # Needs to complete 2 tables to win
            super().__init__(2)
    
        def run(self):
    
            # Print welcome messages
            super().run()
    
            while self.lives > 0 and self.points_to_win > self.points:
                # Gets two random numbers
                number = random.randint(1, 10)            
    
                for i in range(1, 11):
                    
                    if self.lives <= 0:
                        # Ensure that the game can't continue 
                        # if the user depletes the lives
    
                        self.points = 0
                        break 
                    
                    operation = f"{number} x {i}: "
    
                    user_answer = self.get_numeric_input(message=operation)
    
                    if user_answer == number * i:
                        print("Great! Your answer is correct")
                    else:
                        print("Sorry your answer isn't correct") 
    
                        self.lives -= 1
    
                self.points += 1
                
            # Only get executed when the game is finished
            # And none of the conditions are true
            else:
                # Prints the final message
                
                if self.points >= self.points_to_win:
                    self.print_win_message()
                else:
                    self.print_lose_message()

    Como puede darse cuenta, solo estamos modificando el método de ejecución de esta clase. Esa es la magia de la herencia, escribimos una vez la lógica que usamos en varios lugares y nos olvidamos de ella 😅.

    En el método de ejecución, usamos un bucle for para obtener los números del 1 al 10 y construimos la operación que se muestra al usuario.

    Una vez más, si las vidas se agotan o se alcanzan los puntos necesarios para ganar, el ciclo while se romperá y se mostrará el mensaje de ganar o perder.

    SÍ, creamos los dos modos del juego, pero hasta ahora si ejecutamos el programa no pasará nada.

    Así que finalicemos el programa implementando la opción de modo y creando instancias de las clases dependiendo de esa elección.

    Implementación de elección

    El usuario podrá elegir qué modo quiere jugar. Entonces veamos cómo implementarlo.

    if __name__ == "__main__":
    
        print("Select Game mode")
    
        choice = input("[1],[2]: ")
    
        if choice == "1":
            game = RandomMultiplication()
        elif choice == "2":
            game = TableMultiplication()
        else:
            print("Please, select a valid game mode")
            exit()
    
        game.run()

    Primero, le pedimos al usuario que elija entre los modos 1 o 2. Si la entrada no es válida, el script deja de ejecutarse. Si el usuario selecciona el primer modo, el programa ejecutará el Multiplicación aleatoria modo de juego, y si selecciona el segundo, el Multiplicación de tablas se ejecutará el modo.

    Así es como se vería.

    game.png

    Conclusión

    Felicitaciones, solo construir una aplicación Python con Programación Orientada a Objetos.

    Todo el código está disponible en el Repositorio de Github.

    En este artículo aprendiste a:

    • Usa constructores de clases de Python
    • Crea una aplicación funcional con OOP
    • Usa la superfunción en clases de Python
    • Aplicar los conceptos básicos de herencia
    • Implementar atributos de clase e instancia

    Feliz codificación 👨‍💻

    A continuación, explore algunos de los mejor IDE de Python para una mejor productividad.