Neste artigo, você criará um aplicativo de tabelas de multiplicação, usando o poder da Programação Orientada a Objetos (OOP) em Python.
Você praticará os principais conceitos de OOP e como usá-los em um aplicativo totalmente funcional.
Python é uma linguagem de programação multiparadigma, o que significa que nós como desenvolvedores podemos escolher a melhor opção para cada situação e problema. Quando falamos em Programação Orientada a Objetos, estamos nos referindo a um dos paradigmas mais utilizados para construir aplicações escaláveis, nas últimas décadas.
últimas postagens
Noções básicas de OOP
Vamos dar uma olhada rápida no conceito mais importante de OOP em Python, as classes.
Uma classe é um modelo no qual definimos a estrutura e o comportamento dos objetos. Esse template nos permite criar Instâncias, que nada mais são do que objetos individuais feitos a partir da composição da classe.
Uma classe de livro simples, com os atributos de título e cor, seria definida da seguinte forma.
class Book: def __init__(self, title, color): self.title = title self.color = color
Se quisermos criar instâncias do livro de classe, devemos chamar a classe e passar argumentos para ela.
# Instance objects of Book class blue_book = Book("The blue kid", "Blue") green_book = Book("The frog story", "Green")
Uma boa representação do nosso programa atual seria:
O incrível é que, quando verificamos o tipo das instâncias blue_book e green_book, obtemos “Book”.
# Printing the type of the books print(type(blue_book)) # <class '__main__.Book'> print(type(green_book)) # <class '__main__.Book'>
Depois de ter esses conceitos bem claros, podemos começar a construir o projeto 😃.
declaração do projeto
Ao trabalhar como desenvolvedores/programadores, a maior parte do tempo não é gasta escrevendo código, de acordo com thenewstack gastamos apenas um terço do nosso tempo escrevendo ou refatorando código.
Passamos os outros dois terços lendo o código dos outros e analisando o problema em que estamos trabalhando.
Portanto, para este projeto, gerarei uma declaração de problema e analisaremos como criar nosso aplicativo a partir dela. Com isso, estamos fazendo o processo completo, desde pensar na solução até aplicá-la com código.
Um professor primário quer um jogo para testar as habilidades de multiplicação de alunos de 8 a 10 anos.
O jogo deve ter um sistema de vidas e pontos, onde o aluno começa com 3 vidas e deve atingir uma certa quantidade de pontos para vencer. O programa deve mostrar uma mensagem de “perder” se o aluno esgotar todas as suas vidas.
O jogo deve ter dois modos, multiplicações aleatórias e multiplicações de mesa.
A primeira deve dar ao aluno uma multiplicação aleatória de 1 a 10, e ele deve responder corretamente para ganhar um ponto. Caso isso não ocorra o aluno perde uma live e o jogo continua. O aluno só ganha quando atinge 5 pontos.
A segunda modalidade deverá apresentar uma tabuada de 1 a 10, onde o aluno deverá inserir o resultado da respectiva multiplicação. Se o aluno errar 3 vezes perde, mas se completar duas tabelas, o jogo acaba.
Sei que os requisitos podem ser um pouco maiores, mas prometo que vamos resolvê-los neste artigo 😁.
Dividir e conquistar
A habilidade mais importante na programação é a resolução de problemas. Isso ocorre porque você precisa ter um plano antes de começar a hackear o código.
Eu sempre sugiro pegar o problema maior e dividi-lo em problemas menores que podem ser resolvidos de maneira fácil e eficiente.
Portanto, se você precisa criar um jogo, comece dividindo-o nas partes mais importantes. Esses subproblemas serão muito mais fáceis de resolver.
Só assim você pode ter a clareza de como executar e integrar tudo com o código.
Então vamos fazer um gráfico de como seria o jogo.
Este gráfico estabelece as relações entre os objetos de nosso aplicativo. Como você pode ver, os dois objetos principais são Multiplicação aleatória e Multiplicação de tabela. E a única coisa que eles compartilham são os atributos Pontos e Vidas.
Tendo todas essas informações em mente, vamos entrar no código.
Criando a classe de jogo pai
Quando trabalhamos com programação Orientada a Objetos, buscamos a forma mais limpa de evitar a repetição de código. Isso é chamado SECO (não se repita).
Nota: Este objetivo não está relacionado a escrever menos linhas de código (a qualidade do código não deve ser medida por esse aspecto), mas a abstrair a lógica mais usada.
De acordo com a ideia anterior, a classe pai de nossa aplicação deve estabelecer a estrutura e o comportamento desejado das outras duas classes.
Vejamos como seria feito.
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} livesn") def print_current_score(self): print(f"nYour score is {self.points}") def print_description(self): print("nn" + self.description.center(self.message_lenght) + "n") # Basic run method def run(self): self.print_welcome_message() self.print_description()
Uau, isso parece uma classe muito grande. Deixe-me explicar isso em profundidade.
Primeiramente, vamos entender os atributos da classe e o construtor.
Basicamente, atributos de classe, são variáveis criadas dentro da classe, mas fora do construtor ou de qualquer método.
Enquanto os atributos de instância são variáveis criadas apenas dentro do construtor.
A principal diferença entre esses dois é o escopo. ou seja, atributos de classe são acessíveis tanto de um objeto de instância quanto da classe. Por outro lado, os atributos de instância são acessíveis apenas a partir de um objeto de instância.
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
Outro artigo pode mergulhar mais fundo neste tópico. Fique em contato para lê-lo.
A função get_numeric_input é usada para evitar que o usuário forneça qualquer entrada que não seja numérica. Como você pode notar, este método foi projetado para perguntar ao usuário até que ele obtenha uma entrada numérica. Vamos usá-lo mais tarde nas aulas da criança.
Os métodos de impressão nos permitem salvar a repetição de imprimir a mesma coisa cada vez que ocorre um evento no jogo.
Por último, mas não menos importante, o método run é apenas um wrapper que as classes Multiplicação aleatória e Multiplicação de tabela usarão para interagir com o usuário e tornar tudo funcional.
Criando as classes da criança
Depois de criarmos a classe pai, que estabelece a estrutura e algumas das funcionalidades de nosso aplicativo, é hora de criar as classes reais do modo de jogo, usando o poder da herança.
Classe de multiplicação aleatória
Esta classe executará o “primeiro modo” do nosso jogo. Ele vai usar o módulo random, é claro, o que nos dará a capacidade de solicitar ao usuário operações aleatórias de 1 a 10. Aqui está um excelente artigo sobre o random (e outros módulos importantes) 😉.
import random # Module for random operations
class RandomMultiplication(BaseGame): description = "In this game you must answer the random multiplication correctlynYou 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 correctn") # Adds a point self.points += 1 else: print("nSorry, your answer is incorrectn") # 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()
Aqui está outra aula massiva 😅. Mas, como afirmei antes, não é o número de linhas necessárias, é o quanto ele é legível e eficiente. E a melhor coisa sobre o Python é que ele permite que os desenvolvedores criem um código limpo e legível como se estivessem falando inglês normal.
Esta aula tem uma coisa que pode confundi-lo, mas vou explicar da forma mais simples possível.
# 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)
O construtor da classe filha está chamando a função super que, ao mesmo tempo, se refere à classe pai (BaseGame). É basicamente dizer ao Python:
Preencha o atributo “points_to_win” da classe pai com 5!
Não é necessário colocar self, dentro da parte super().__init__() só porque estamos chamando super dentro do construtor, e isso resultaria em redundância.
Também estamos usando a função super no método run e veremos o que está acontecendo naquele trecho 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 você pode observar o método run na classe pai, imprima a mensagem de boas-vindas e descrição. Mas é uma boa ideia manter essa funcionalidade e também adicionar outras extras nas classes filhas. De acordo com isso, usamos super para executar todo o código do método pai antes de executar a próxima parte.
A outra parte da função de execução é bastante direta. Solicita ao usuário um número com a mensagem da operação que deve responder. Depois o resultado é comparado com a multiplicação real e se forem iguais soma um ponto, se não tirar 1 vida.
Vale dizer que estamos usando loops while-else. Isso excede o escopo deste artigo, mas publicarei um sobre isso em alguns dias.
Finalmente, get_random_numbers, usa a função random.randint, que retorna um número inteiro aleatório dentro do intervalo especificado. Em seguida, ele retorna uma tupla de dois inteiros aleatórios.
Classe de multiplicação aleatória
O “segundo modo” deve exibir o jogo em formato de tabuada e garantir que o usuário acerte pelo menos 2 tabuadas.
Para isso, usaremos novamente o poder de super e modificaremos o atributo da classe pai points_to_win para 2.
class TableMultiplication(BaseGame): description = "In this game you must resolve the complete multiplication table correctlynYou 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 você pode perceber, estamos apenas modificando o método run desta classe. Essa é a mágica da herança, escrevemos uma vez a lógica que usamos em vários lugares e esquecemos 😅.
No método run, estamos usando um loop for para obter os números de 1 a 10 e construímos a operação que é mostrada ao usuário.
Mais uma vez, se as vidas forem esgotadas ou os pontos necessários para vencer forem alcançados, o loop while será interrompido e a mensagem de vitória ou derrota será exibida.
SIM, criamos os dois modos do jogo, mas até agora se rodarmos o programa nada vai acontecer.
Então, vamos finalizar o programa implementando a escolha do modo e instanciando as classes dependendo dessa escolha.
Implementação de escolha
O usuário poderá escolher qual modo quer jogar. Então, vamos ver como implementá-lo.
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()
Primeiro, pedimos ao usuário que escolha entre os modos 1 ou 2. Se a entrada não for válida, o script para de funcionar. Se o usuário selecionar o primeiro modo, o programa executará o modo de jogo Multiplicação Aleatória, e se selecionar o segundo, o modo Tabela de multiplicação será executado.
Aqui está como seria.
Conclusão
Parabéns, você só construir um aplicativo Python com Programação Orientada a Objetos.
Todo o código está disponível no Repositório Github.
Neste artigo você aprendeu a:
- Usar construtores de classe Python
- Crie um aplicativo funcional com OOP
- Use a função super nas aulas do Python
- Aplicar os conceitos básicos de herança
- Implementar atributos de classe e instância
Feliz codificação 👨💻
Em seguida, explore alguns dos melhores IDE Python para obter melhor produtividade.