본문 바로가기

Python

4. 클래스(객체 지향 프로그래밍 Object Oriented Programming)

클래스를 설명하자면, 변수와 함수를 묶어서 나타낸 새로운 객체 라고 생각하면 된다. 예를 들자면 건물 도면 같은 아이다. 건물 설계 도면을 보면 방, 화장실, 주방, 거실 등 여러 분야로 나눠져있지만 이 건물도면이 작업자들에게 있으면 정말 똑같은 건물을 하나 더 만들 수 있다. 클래스를 쉽게 설명하자면 방, 화장실, 주방, 거실등이 '함수'로 쓰이고 이걸 총괄하고 있는 건물 설계 도면을 '클래스'라고 생각하면 된다. 그리고 그 결과물을 '인스턴스'라고 부른다. 사실 클래스가 파이썬 프로그래밍 자체에서 제일 어렵다고 생각한다. 그래서 한 번보고 이해한게 아닌, 여러번 공부를 해야지 클래스를 익힐 수 있다.

#클래스의 기본 구조 
class 클래스이름(상속클래스명):
클래스변수1
클래스변수2
...
def 클래스 함수1(self,인수1,인수2):
	수행할문장1
	수행할문장2

def 클래스 함수2(self,인수1,인수2):
	수행할문장1
	수행할문장2

def 클래스 함수3(self,인수1,인수2):
	수행할문장1
	수행할문장2

 

정말 간단한 예시를 들어보았다. 현재는 햄버거 주문을 받는 키오스크 서비스를 본따서 만들어봤다. 현재 레스토랑에 메뉴가 햄버거 밖에 없다는 가정이다. 가만보면 self라는 새로운 명령어가 보일 것 이다. 정말 깊게 들어가자면 메모리 할당위치와  깊은 관련이 있는데, 굳이 이해할 필요는 없으므로 클래스 내 함수의 첫번째 인수는 무조건 self로 사용해야 함수를 사용할 수 있다. 'customer = Foodorder()' 이 부분은 손님이  클래스에 가입한다고 이해하는 게 쉬울 것이다. 

#클래스 예시, 음식점 키오스크
class Foodorder: #Foodorder라는 클래스 지정
    
    def Hamburger(self,a): #햄버거 함수
        print('햄버거 %s개 주문 완료되었습니다.'%a) #출력값
    
            
customer = Foodorder() #손님이 키오스크 사용

count = int(input('햄버거를 몇개 주문 하시겠습니까?')) #입력값
customer.Hamburger(count) #출력, 입력값에 따라 함수 결과가 달라짐
#or
Foodorder.Hamburger(customer, count) #출력문을 또 다른 방법으로 만들 수 있다.

#>> '햄버거 2개 주문 완료되었습니다.'

여기서 손님이 많아져, 몇번째 손님인지 알아야한다. 그래서 직원이 손님의 순서를 정해주고, 주문을 받아야하는 상황에 이르렀다.  아래 식을 보면 customer.set_sequence(sequence)라는 코드가 있다. 이 말은 sequence가 손님의 정보라면, 클래스에 손님의 정보를 저장하여, set_sequence함수에 응용하여, self.sequence를 사용해 hamburger 함수에서 출력을 시키는 원리다. 하지만 __init__ 함수를 사용하면 손님의 정보를 더욱 쉽게 받아낼 수 있다. __init__함수를 사용하면, customer = Foodorder(sequence) 이것만 기입해도, 위와 같은 식의 역할을 해내기 때문이다. __init__은 인스턴스를 만들 때, 항상 실행되는 것이다. 우리는 손님의 정보, __init__함수를 '속성' 이라고 부른다. 왜냐하면 손님이 뭔가를 주문할때, 항상 손님의 정보가 있어야하니까. 그리고 손님이 주문한 내용의 함수들을  '메서드' 라고 칭한다. 

#__init__ 함수 쓰기전
class Foodorder:
    
    def set_sequence(self, sequence):
        self.sequence = sequence
        
    def Hamburger(self, a):
        print('%s번째 고객님, 햄버거 %s개 주문 완료되었습니다.'%(self.sequence, a))
    
sequence = int(input('몇번째 고객이십니까?')) #입력
count = int(input('햄버거를 몇개 주문 하시겠습니까?')) #입력   

customer = Foodorder() #클래스 사용
customer.set_sequence(sequence)#클래스에 등록되어 있는 
							   #customer에 set_sequence함수에 입력값 넣기

customer.Hamburger(count)#출력




#__init__이라는 함수를 쓴 이후
class Foodorder: #클래스지정
    
    def __init__(self, sequence): 
        self.sequence = sequence
        
    def Hamburger(self, a):
        print('%s번째 고객님, 햄버거 %s개 주문 완료되었습니다.'%(self.sequence, a))
    
sequence = int(input('몇번째 고객이십니까?'))
count = int(input('햄버거를 몇개 주문 하시겠습니까?'))

customer = Foodorder(sequence) #클래스 지정할때, 순서 지정도 가능 굳이
						       #customer.set_sequence(sequence)라는 코드 안써도됨

customer.Hamburger(count)

여태까지 배운 걸로, 사칙연산이 가능한 계산기를 만들어보았다. 이걸 다 이해할 수 있다면, 클래스의 기본 개념은 다 잡혔다고 생각한다.

class calc: #계산기 클래스
    def __init__(self, first, second): #__init__함수 사용
        self.first = first
        self.second = second
    
    def sum(self): #더하기일 때
        return print("%d + %d = %d"%(self.first, self.second, self.first + self.second))
    
    def sub(self): #뺄셈일 때
        return print("%d - %d = %d"%(self.first, self.second, self.first - self.second))
        
    def mul(self): #곱하기일 때
        return print("%d * %d = %d"%(self.first, self.second, self.first * self.second))
    
    def div(self): #나누기일 때
        return print("%d / %d = %d"%(self.first, self.second, self.first / self.second))
    

first,b,second = map(str, input('계산기를 입력하시오:').split(' ')) #입력란
first,second = int(first), int(second) #숫자들 정수로 변환

user = calc(first, second) #첫번째 숫자외 두번째 숫자 __init__안에 입력 및 클래스 사용 

if b == '+': #기호에 따라 출력 값 다름
    user.sum()
elif b == '-':
    user.sub()
elif b == '*':
    user.mul()
elif b == '/':
    user.div()
else: #기호가 이상한 기호면, '잘못된 입력입니다.' 출력
    print('잘못된 입력입니다.')

클래스는 서로 상속할 수 있다. 상속(Inheritance)이라함은, 우리가 평소에 아는 재산 상속과 같은 의미에 상속이다. 햄버거집의  사장님이 장사가 너무 잘되셔서 햄버거와 피자를 같이 팔기로 결정했다. 햄버거키오스크를 피자키오스크랑 같이 사용할 수 있도록 상속 시킬 예정이다.  클래스 상속을 시킬때는 아래와 같은 문법을 사용한다.

class 상속받을 클래스명(상속할 클래스명)

클래스 상속의 예시이다. 앞에 상속할 클래스명만 넣어도 똑같이 사용할 수 있다. 

class kiosk: #kiosk라는 클래스 생성
    food = '버거' #버거 키오스크임
    
    def __init__(self, burger): #__init__함수 사용
        self.full_order = burger + self.food #종류 입력시, xx버거 이런식으로 나올 수 있게 해놈
    

    def order(self,count): #함수사용
        return print('주문하신 {0}, {1}개 나왔습니다.'.format(self.full_order, count))
 			#위에서 만든 self.full_order와 밖에서 입력 받은 count로 출력
 
burger= str(input('무슨 종류의 버거를 원하십니까? ')) #입력문
count= str(input('몇개를 주문하시겠습니까? ')) #입력문

customer = kiosk(burger) #__init__함수에 들어갈 인수 기입
customer.order(count) #order함수에 들어갈 인수 기입

class pizzakiosk(kiosk): #위에 Kiosk클래스를 피자집에서도 사용
    food = '피자' #버거 키오스크가 아닌 피자 키오스크

burger= str(input('무슨 종류의 피자를 원하십니까? ')) #위와 같이 입력문
count= str(input('몇판을 주문하시겠습니까? ')) #입력문
pizzacustomer = pizzakiosk(burger)
pizzacustomer.order(count)
>>무슨 종류의 버거를 원하십니까?  치즈
몇개를 주문하시겠습니까?  2
주문하신 치즈버거, 2개 나왔습니다.
무슨 종류의 피자를 원하십니까? 불고기
몇판을 주문하시겠습니까? 2
주문하신 치즈피자, 1개 나왔습니다.

위에 상속받은 클래스의 예시를 보면 문제가 한가지있다. 우리는 피자를 '판'으로 세지, '개'로 세지는 않는다. 그렇다는 말은, 클래스를 수정해줘야된다는 것인데, 그렇다면 버거집의 키오스크가 문제가 생길 것이다. 이럴땐 '매서드 오버라이딩' 이라는 문법을 사용한다. 하는 법은 매우 쉽다. class pizzakiosk(kiosk): 안에 함수(메서드) 이름을 동일하게 하여, 새로운 함수를 만들면 된다.

class kiosk:
    food = '버거'
    
    def __init__(self, burger):
        self.full_order = burger + self.food
    

    def order(self,count):
        return print('주문하신 {0}, {1}개 나왔습니다.'.format(self.full_order, count))
 
 
burger= str(input('무슨 종류의 버거를 원하십니까? '))
count= str(input('몇개를 주문하시겠습니까? '))

customer = kiosk(burger)
customer.order(count)

class pizzakiosk(kiosk):
    food = '피자'
    def order(self,count): #함수이름은 같지만, '개'에서 '판'으로 수정
        return print('주문하신 {0}, {1}판 나왔습니다.'.format(self.full_order, count))
    
burger= str(input('무슨 종류의 피자를 원하십니까? '))
count= str(input('몇판을 주문하시겠습니까? '))
pizzacustomer = pizzakiosk('치즈')
pizzacustomer.order('1')
>>무슨 종류의 버거를 원하십니까? 치즈
몇개를 주문하시겠습니까? 2
주문하신 치즈버거, 2개 나왔습니다.
무슨 종류의 피자를 원하십니까? 콤비네이션 
몇판을 주문하시겠습니까? 1
주문하신 치즈피자, 1판 나왔습니다. #수정완료

연산자 오버로딩(overloading)이란 객체끼리 사용할 수 있게 하는 기법이다. 연산자 (+,-,*,/)를 객체 끼리 사용하는 것이다. 예를 들어, 햄버거집 사장님이 햄버거와 피자를 다 같이 주문하는 손님을 위해서 서비스를 주기로 결심을 하였는데, 미성년자는 감자튀김과 콜라, 성인은 감자튀김과 맥주를 주기로 결심하였다. 연산자 오버로딩을 사용하기 위해서는 원래 class인 kiosk class에 __add__함수를 쓰고, 함수인수에 self, other을 기입하여 작성해준다. 그리고 문자열 포매팅을 할때는 'other.' 을 사용하여, 다른 클래스에 있는 객체를 불러온다. __add__ 함수를 사용하는 이유는 밑에보면, pizzacustomer + customer 끼리 객체를 더해주었기 때문이다. 이와 같은 원리로, 빼기 일때는 __sub__, 곱하기일때는 __mul__, 나누기일때는 __div__을 사용한다. pizzacustomer + customer를 사용하면 __add__ 함수가 사용되고, pizzacustomer.underage(customer)를 사용하면, pizzacustomer에 있는 underage함수를 customer와 함께 사용한다는 뜻이다. 빼기도 물론 같다.

class kiosk:
    food = '버거'
    
    def __init__(self, burger):
        self.full_order = burger + self.food
    

    def order(self,count):
        return print('주문하신 {0}, {1}개 나왔습니다.'.format(self.full_order, count))
    
    def __add__(self, other): #__add__함수를 사용하여, 연산자 오버로딩 사용
        return print('{0}와 {1}를 시키셔서, 감자튀김과 맥주는 서비스입니다.'.format(self.full_order, other.full_order))
    
    def underage(self,other): #이미 __add__함수를 사용했음으로, 평소 함수처럼 넣으면됨. 
        return print('{0}와 {1}를 시키셔서, 감자튀김과 콜라는 서비스입니다.'.format(self.full_order, other.full_order))
 
 
burger= str(input('무슨 종류의 버거를 원하십니까? '))
count= str(input('몇개를 주문하시겠습니까? '))

customer = kiosk(burger)
customer.order(count)

class pizzakiosk(kiosk):
    food = '피자'
    def order(self,count):
        return print('주문하신 {0}, {1}판 나왔습니다.'.format(self.full_order, count))
    
burger= str(input('무슨 종류의 피자를 원하십니까? '))
count= str(input('몇판을 주문하시겠습니까? '))
age = int(input('나이가 어떻게 되십니까? '))

pizzacustomer = pizzakiosk(burger)
pizzacustomer.order(count)

if age < 20:
    pizzacustomer.underage(customer) #미성년자일경우
else:
    pizzacustomer + customer #서비스