Dev

[Python] 2. 객체 지향 프로그래밍 (feat. OX 퀴즈 게임)

mlslly 2024. 5. 3. 08:38

* Udemy의 PythonBootcamp 수업 내용을 참고하여 작성

 

파이썬은 객체지향 프로그래밍(Objected Oriented Programming)을 지원하는 언어로써 클래스를 사용하여 객체를 정의하고 이를 통해 프로그램을 구성한다. 

즉 클래스와 객체 등의 개념이 곧 객체 지향 프로그래밍의 핵심이라고 볼 수 있다. 

오늘은 클래스를 만들고, 이를 활용해 퀴즈 게임을 만드는 예시 프로그래밍까지 해볼 것이다.

 


파이썬 클래스 만들기 

클래스 Class 

 

클래스는 객체를 생성하기 위한 틀이자 설계도이다.  클래스는 객체의 속성(attribute)과 메서드(method)를 정의한다.

클래스를 만드는 이유는 반복적으로 객체를 생성하고 속성을 부여해야 할 때가 있기 때문이다.

 

아래는 User라는 빈 클래스를 생성 후, user1이라는 객체를 만들고 그 객체에 .id와 .username 이라는 속성을 부여한 것이다.

만약 이것과 동일한 작업을 id 100까지 반복해야 한다면? 클래스라는 틀이 있다면 매번 객체에 처음부터 작업할 필요가 없다.

class User: 
    pass 

user1 = User()
user1.id = '001'
user1.username = 'angela'

print(user1.username)

 

init 생성자 

 

파이썬에서 객체들을 묶어 작동시키는 constructor를 만들려면, __init___ 생성자라는 특수한 생성자를 만들어야 한다. 

init은 '속성을 initialize'하는 역할을 하며, 클래스의 객체가 실행될 때마다 소환된다. 

따라서 init 생성자를 정의하고 특정 문구를 print하게 했다면, 객체가 생성될때마다 그 문구가 나타날 것이다.

class User: 
    def __init__(self) :
            print('new user being created...')
    pass 

user1 = User()
user1.id = '001'
user1.username = 'angela'

user2 = User()
user2.id = '002'
user2.username = 'jack'

 

user1 = User() 이 실행될 때, 또 user2 = User() 이 실행될때 각각 문구가 나타나는 것을 볼 수 있다.

 

속성 attributes 

 

생성자에 속성 주기. 클래스를 통해 initialize되는 객체에 속성을 부여하려면 어떻게 해야 할까?

self.속성 으로 객체에 속성을 부여해주면 된다. 또한 파라미터로 객체마다 속성 값을 받아 부여해줄 수 있다.

그리고 속성들은 객체가 실행될 때마다 초기화된다.

class User: 
    def __init__(self, user_id, username) :
            self.id = user_id
            self.username = username

user_1 = User('001', 'angela') 
print(user_1.id)

 

이처럼, user객체에 파라미터를 넣어줌으로써 id와 username이라는 속성을 부여했다.

이는 아래 코드처럼 객체에 직접 속성을 부여한 것과 동일한 효과를 가진다. 3줄짜리 코드를 한줄로 만들 수 있는 것. 

이런 효율성이 클래스 활용의 효용성이다.

user_1 = User()

user_1.id = '001'
user_1.username = 'angela'

 

파라미터 parameter

 

만약 파라미터를 생성했다면, 앞으로 만드는 모든 User 객체는 id와 username이라는 파라미터를 받아야만 정상적으로 생성된다.

그러나 모든 User에 없는 속성일 경우 디폴트 값으로 속성을 정의할 수도 있다.

예를 들어 팔로워라는 속성을 User 객체에 부여하고자 하고, 기본적으로 팔로워는 0인 초기값을 가진다면, User 클래스는 follower 파라미터 없이 아래와 같이 정의될 수 있다.

class User(user_id, username) : 
    self.id = user_id
    self.username = username
    self.follower = 0

 

 

메소드 methods

 

속성이 객체가 가진 것이라면, 메소드는 객체가 '하는 것' 이다.

아래 '팔로우' 메소드를 유저 클래스에 추가한 경우를 보자.

'follow' 메소드는 내가 팔로우할 유저를 입력받아, 그 사람을 팔로우하게 되면, 내 팔로잉은 +1 증가하고, 그 사람의 팔로우가 +1 증가하는 기능이다.

class User: 
    def __init__(self, user_id, username) :
        self.id = user_id
        self.username = username 
        self.followers = 0
        self.following = 0

    
    def follow(self, user): 
        '''팔로우 기능, 유저의 팔로워가 +1, 내 팔로잉 +1'''
        user.followers += 1
        self.following += 1

user_1 = User('001', 'angela')
user_2 = User('002','jack')

user_1.follow(user_2)
print('user_1의 팔로워 수', user_1.followers)
print('user_1의 팔로잉 수', user_1.following)
print('user_2의 팔로워 수', user_2.followers)
print('user_2의 팔로잉 수', user_2.following)


OX 퀴즈 게임 만들기 

1. 퀴즈의 Question 클래스 만들기 

init 메소드에서 text와 answer 두개를 속성으로 갖는 문제 클래스를 만들기 

 

<question_model.py 파일 내용>

class Question:
    def __init__(self, q_text, q_answer) :
        self.text = q_text
        self.answer = q_answer

 

2. 퀴즈의 Question bank 만들기 

퀴즈에 사용할 질문들의 묶음을 만들어보자.

question_data 리스트를 순회하면서 각 질문에 대해서 Question 클래스 객체를 생성하고, question_bank 리스트에 모든 Question 객체를 담기

 

<main.py 파일 내용>

from question_model import Question
from data import question_data

question_bank = []
for question in question_data : 
    question_text = question['text']
    question_answer = question['answer']
    new_question = Question(question_text, question_answer)
    
    question_bank.append(new_question)

print(question_bank)

 

문제별 개별 질문, 답변은 아래와 같이 확인 가능 

print(question_bank[0].text) 
print(question_bank[0].answer)

 

3. 사용자와의 상호작용 코드 

 

사용자에게 현재의 질문번호와 질문내용을 보여주고, 사용자로 하여금 답변하게 하는 코드를 만들자.

current_question이라는 변수를 만들어서, 질문 리스트중 number의 인덱스 내용을 가져오면 된다.

이때 current_question 이라는 변수는 self.question_list이고, question_list는 앞서 question_bank로부터 가져온 것이므로 .text와 .answer이라는 속성을 그대로 가져와 사용할 수 있다.

 

<quiz_brain.py 파일 내용>

class QuizBrain : 
    def __init__(self, q_list):
        self.question_number = 0
        self.question_list = q_list

    def next_question(self) :
        current_question = self.question_list[self.question_number]
    	self.question_number += 1
        input(f'Q.{self.question_number}: {current_question.text} (True/False): ')

 

4. 퀴즈 실행하기 

 

다시 main.py로 돌아와서

<main.py 파일 내용 >

from question_model import Question
from data import question_data
from quiz_brain import QuizBrain

question_bank = []
for question in question_data : 
    question_text = question['text']
    question_answer = question['answer']
    new_question = Question(question_text, question_answer)

    question_bank.append(new_question)


quiz = QuizBrain(question_bank)
quiz.next_question()

 

5. 질문이 계속 이어지도록 만들기 

 

QuizBrain 클래스의 메소드로, 아직 문제가 남았는지 확인하는 'still_has_questions' 를 추가한다.

이 메소드는 아직 질문이 남아있다면 True를, 모든 질문이 실행되었다면 False를 반환한다.

그리고 나서 main.py에서 still_has_questions 메소드가 True인 한 계속 다음 질문이 생성되도록 next_question을 실행시킨다.

 

<quiz_brain.py 파일 내용>

class QuizBrain : 
    def __init__(self, q_list):
        self.question_number = 0
        self.question_list = q_list


    def still_has_questions(self):
        if self.question_number < len(self.question_list) :
            return True
        else : 
            return False

    def next_question(self) :
        current_question = self.question_list[self.question_number]
        self.question_number += 1 # 번호 증가
        input(f'Q.{self.question_number}: {current_question.text} (True/False): ')

 

<main.py 파일 내용>

from question_model import Question
from data import question_data
from quiz_brain import QuizBrain

question_bank = []
for question in question_data : 
    question_text = question['text']
    question_answer = question['answer']
    new_question = Question(question_text, question_answer)

    question_bank.append(new_question)

quiz = QuizBrain(question_bank)

while quiz.still_has_questions():   
    quiz.next_question()

 

6. 답변 확인 및 점수 기록 

 

답변이 정답과 동일한지 확인하는 check_answer 메소드를 추가한다. 

스코어 속성을 __init__ 생성자에 추가하고, 정답을 맞출때마다 score을 1씩 증가한다.

현재 사용자의 점수를 알려준다.

 

<quiz_train.py 파일 내용>

class QuizBrain : 
    def __init__(self, q_list):
        self.question_number = 0
        self.question_list = q_list
        self.score = 0

    def still_has_questions(self):
        return self.question_number < len(self.question_list) 

    def next_question(self) :
        current_question = self.question_list[self.question_number]
        self.question_number += 1 # 번호 증가
        user_answer = input(f'Q.{self.question_number}: {current_question.text} (True/False): ')
        self.check_answer(user_answer, current_question.answer) # check_answer라는 속성을 만들어주기

    def check_answer(self, user_answer, correct_answer): # check_answer에 변수로 전달됨
        if user_answer.lower() == correct_answer.lower():  # 대소문자 구분없이
            self.score += 1
            print('You got it right')

        else : 
            print('That\'s wrong')
            
        print(f'The correct answer is {correct_answer}')
        print(f'Your current score is : {self.score}/{self.question_number}')
        print()

 

다시 아래의 main.py 파일을 실행하면 아래와 같이 점수 및 문구도 함께 출력된다.

마지막 줄에 quiz 객체의 속성을 활용하여 quiz.score, quiz.question_number를 불러와 전체 점수를 기록할 수 있다.

<main.py 파일 내용>

from question_model import Question
from data import question_data
from quiz_brain import QuizBrain

question_bank = []
for question in question_data : 
    question_text = question['text']
    question_answer = question['answer']
    new_question = Question(question_text, question_answer)

    question_bank.append(new_question)

quiz = QuizBrain(question_bank)

while quiz.still_has_questions():   
    quiz.next_question()

print('You\'ve completed the quiz')
print(f'Your final score was: {quiz.score}/ {quiz.question_number}')

 

...

 

참고. 퀴즈게임 확장하기 

 

예시 퀴즈들말고, 더 많은 퀴즈게임 데이터베이스를 활용하여 게임을 확장시켜 보자. 

활용할 데이터베이스는 TRIVIA라는 무료 오픈소스 퀴즈 사이트. 

Trivia API 사이트로 가서, 퀴즈의 개수와 카테고리를 선택한 뒤 'Generate API URL'로 들어가면 퀴즈가 json 파일 형태로 확인된다.

https://opentdb.com/api_config.php

 

Open Trivia DB

Free to use, user-contributed trivia questions!

opentdb.com

 

data 파일에 데이터를 바꿔 넣기만 하면, quiz brain 파일의 수정 없이, main 파일에서의 question['question'] 부분만 조정만 해주면  퀴즈 게임을 무한히 확장할 수 있다.

이것이 파이썬의 모듈 modularity의 장점이자 객체 지향의 장점이다. 동시적으로 파일을 서로 다른 사람이 수정해도 문제 없다.

quiz brain의 내용들이 퀴즈 게임의 기능을 수행하는 한, 퀴즈의 종류, 데이터 등은 계속 바뀌고 확장될 수 있다.