문제

APC에 온 것을 환영한다. 만약 여러분이 학교에서 자료구조를 수강했다면 해시 함수에 대해 배웠을 것이다. 해시 함수란 임의의 길이의 입력을 받아서 고정된 길이의 출력을 내보내는 함수로 정의한다. 해시 함수는 무궁무진한 응용 분야를 갖는데, 대표적으로 자료의 저장과 탐색에 쓰인다.

이 문제에서는 여러분이 앞으로 유용하게 쓸 수 있는 해시 함수를 하나 가르쳐주고자 한다. 먼저, 편의상 입력으로 들어오는 문자열에는 영문 소문자(a, b, ..., z)로만 구성되어있다고 가정하자. 영어에는 총 26개의 알파벳이 존재하므로 a에는 1, b에는 2, c에는 3, ..., z에는 26으로 고유한 번호를 부여할 수 있다. 결과적으로 우리는 하나의 문자열을 수열로 변환할 수 있다. 예를 들어서 문자열 "abba"은 수열 1, 2, 2, 1로 나타낼 수 있다.

해시 값을 계산하기 위해서 우리는 문자열 혹은 수열을 하나의 정수로 치환하려고 한다. 간단하게는 수열의 값을 모두 더할 수도 있다. 해시 함수의 정의에서 유한한 범위의 출력을 가져야 한다고 했으니까 적당히 큰 수 M으로 나눠주자. 짜잔! 해시 함수가 완성되었다. 이를 수식으로 표현하면 아래와 같다.

 

해시 함수의 입력으로 들어올 수 있는 문자열의 종류는 무한하지만 출력 범위는 정해져있다. 다들 비둘기 집의 원리에 대해서는 한 번쯤 들어봤을 것이다. 그 원리에 의하면 서로 다른 문자열이더라도 동일한 해시 값을 가질 수 있다. 이를 해시 충돌이라고 하는데, 좋은 해시 함수는 최대한 충돌이 적게 일어나야 한다. 위에서 정의한 해시 함수는 알파벳의 순서만 바꿔도 충돌이 일어나기 때문에 나쁜 해시 함수이다. 그러니까 조금 더 개선해보자.

어떻게 하면 순서가 달라졌을때 출력값도 달라지게 할 수 있을까? 머리를 굴리면 수열의 각 항마다 고유한 계수를 부여하면 된다는 아이디어를 생각해볼 수 있다. 가장 대표적인 방법은 항의 번호에 해당하는 만큼 특정한 숫자를 거듭제곱해서 곱해준 다음 더하는 것이 있다. 이를 수식으로 표현하면 아래와 같다.

 

 

보통 r과 M은 서로소인 숫자로 정하는 것이 일반적이다. 우리가 직접 정하라고 하면 힘들테니까 r의 값은 26보다 큰 소수인 31로 하고 M의 값은 1234567891(놀랍게도 소수이다!!)로 하자.

이제 여러분이 할 일은 위 식을 통해 주어진 문자열의 해시 값을 계산하는 것이다. 그리고 이 함수는 간단해 보여도 자주 쓰이니까 기억해뒀다가 잘 써먹도록 하자.

 

 

처음 작성한 코드) 런타임 에러

M = 1234567891
num = int(input())
word = list(map(str, input()))
answer = 0

for i in range(num):
    answer += ord(word[i]-96) * (31**i)

print(answer%M)

우선 런타임 에러로 실패한 코드인데 알고리즘을 먼저 설명을 해보도록 한다.

결론은

이 수식을 코드화 하는 것!

생각보다 어렵지 않다.

 

1. 입력한 알파벳을 숫자화 시킨다

 => ord()함수를 이용하여 아스키코드로 변환. 하지만 소문자의 아스키코드는 97부터 시작하기 때문에 a를 1로 만들고 싶다면 96을 빼주어야 한다.

 

2. 숫자로 변환한 값에 r=31(문제에서 r=31이었음)의 거듭제곱을 곱해준다.

 => for문을 이용하여 거듭제곱을 늘려주었다.

 

3. 곱해준 갑들을 다 더해준다.

 => answer에 += 해주었다. 

 

4. 더한 값을 M=1234567891(문제에서 주어짐)로 나누어 출력

  

 

이상 코드 설명은 끝났다.

런타임에러가 나왔기 때문에 최대한 코드를 줄여보려 했는데....

 

num = int(input())
word = list(map(str, input()))
answer = 0

for i in range(num):
    answer += ord(word[i]-96) * (31**i)

print(answer%1234567891)

뭘...더 해야하노....

 

 

 

num = int(input())
word = input()
answer = 0

for i in range(num):
    answer += ord(word[i]-96) * (31**i)
print(answer%1234567891)

우선 사람들 코드를 힌트를 얻어 word를 list(map(str, input) 하지 않고서도 그냥 input하고 for문을 하면 알파벳들이 찢어져서 출력되는 것이 확인 되었다.

근데 이것도 런타임 에러인데?

대환장

 

 

 

+) 문제를 찾았다.

answer += ord(word[i]-96) * (31**i)

도대체 word[i]에서 96을 빼는 게 말이 되냐고ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

 

 

num = int(input())
word = input()
answer = 0

for i in range(num):
    answer += (ord(word[i])-96) * (31**i)
print(answer%1234567891)

다시 제정신 차리고 수정해서 100점을 받았다~^^

 

'Algorithm' 카테고리의 다른 글

programmers #조이스틱  (0) 2021.09.15
programmers #정수삼각형  (0) 2021.08.22
백준 10829번 #이진수변환  (0) 2021.08.21
programmers #[1차] 비밀지도  (0) 2021.08.19
백준 5692번 #팩토리얼 진법  (0) 2021.08.17

문제

자연수 N이 주어진다. N을 이진수로 바꿔서 출력하는 프로그램을 작성하시오.

 

 

n = int(input())
arr = []

def binary(n):
    if n == 1:
        arr.append(1)    # n=1 이면 이진수 변환값은 1
    elif n  == 2:
        arr.append(0)
        arr.append(1)    #n=2이면 이진수 변환값은 10인데 pop하기위해 01로 저장
    else:
        arr.append(n%2)  #n%2를 먼저 저장하고 
        binary(n//2)     #다시 함수 호출(재귀함수 사용)

binary(n)

for _ in range(len(arr)):
    print(arr.pop(), end='')   #pop해서 배열 순서 뒤집기

 

파이썬에는 bin함수가 있지만 재귀함수를 이용해서 푸는 것이 문제의 포인트였던 것 같아 알고리즘을 구현했다. n이 3 이상의 수라면 재귀함수를 이용하여 나머지들을 리스트에 저장하게끔 하였고 n이 2이하라면 직접 이진수값을 배열에 입력하는 식으로 구현했다.

조금 더 한줄 쓰기에 접근하려면 2이하의 수들도 if문 하나로 짤 수 있었겠지만 머리쓰기가 귀찮았다...ㅎ

 

그리고 배열들을 역순배치를 할까 하다가 역순->int->join 으로 하면 코드가 길어질 것 같아 pop을 이용할 수 있다는 방법을 적용해보았다!!!

재귀함수 아직도 어렵네

'Algorithm' 카테고리의 다른 글

programmers #정수삼각형  (0) 2021.08.22
백준 15829번 #Hashing  (0) 2021.08.22
programmers #[1차] 비밀지도  (0) 2021.08.19
백준 5692번 #팩토리얼 진법  (0) 2021.08.17
백준 10872 #팩토리얼  (0) 2021.08.17

Q.

함수 solution은 정수 n을 매개변수로 입력받습니다. n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요. 예를들어 n이 118372면 873211을 리턴하면 됩니다.

 

def solution(n):
    return int("".join(sorted(list(str(n)), reverse=True)));

이건 코드 자체를 공부해야한다.

지금 부족한 점은

1. 함수를 모름( sorted(reverse=True), join 이 뭔지 모름)

2. 간단명료하게 가로로 늘여 쓰는 것을 잘 못함

 

스터디 조원의 도움을 받아서 짜낸 코드가

def solution(n):
    m = list(str(n))
    m.sort(reverse=True)
    res = "".join(m)
    return int(res)

ㅋ....조인은 sql에서도 지랄이더니 여기서도ㅋ..

Q.

문자열 s의 길이가 4 혹은 6이고, 숫자로만 구성돼있는지 확인해주는 함수, solution을 완성하세요. 예를 들어 s가 "a234"이면 False를 리턴하고 "1234"라면 True를 리턴하면 됩니다.

 

 

def solution(s):
    answer = True
    if len(s) == 4 or len(s)==6:
        for i in s:
            if i not in '0123456789':
                answer = False
    else:
        answer = False
        
    return answer

 

def alpha_string46(s):
    return s.isdigit() and len(s) in (4, 6)

isdigit() 숫자인지 확인하기

isalpha() 알파벳인지 확인하기 

Q. 자연수 n을 뒤집어 각 자리 숫자를 원소로 가지는 배열 형태로 리턴해주세요. 예를들어 n이 12345이면 [5,4,3,2,1]을 리턴합니다.

 

def solution(n):
    m = list(str(n))
    num = list(map(int, m))
    l = len(m)
    a = [0]*l
    for i in range(0, l):
        for j in range(0, l):
            if i != j and i + j == (l-1):
                a[i]=num[j]
                a[j]=num[i]
            elif i == j and i + j == (l-1):
                a[i] = num[i]
    return a

 

Sol)

우선 자연수 n이 13569 라 가정한다.

우리는 [9,6,5,3,1]을 결과값으로 출력해야 한다. 

n을 각 자리 수마다 쪼개서 리스트에 담았다. 그리고 리스트를 다음과 같이 표현했다.

idx 0 1 2 3 4
1 3 5 6 9

결과값을 분석해보면

1과 9가 자리를 바꿨고, 3과 6이 자리를 바꿨다. 다시 말해

n[0]과 n[4]가 자리를 바꿨고, n[1]과 n[3]이 자리를 바꿔야한다.

자리를 바꿔야하는 인덱스들의 공통점은 합이 리스트 n의 길이 (또는 자연수 n이 자릿수) - 1과 같다. 

 

def solution(n):
    m = list(str(n))   #자연수 n을 각 자리수마다 쪼개서 list에 담기(문자열로만 담김)
    num = list(map(int, m))  #m에 담긴 값을 다시 int로 바꿔 num에 담기
    l = len(m)      #리스트 m의 길이
    a = [0]*l       #조건을 비교하여 집어넣을 빈 리스트 
    for i in range(0, l):
        for j in range(0, l):
            if i != j and i + j == (l-1):   # ex) l=5일 때, i=0, j=4이면
                a[i]=num[j]					# 자리가 바뀌게끔 a에 추가 
                a[j]=num[i]
            elif i == j and i + j == (l-1):  # ex) l=5일 때, i=2, j=2이면
                a[i] = num[i]				# 자리 바꿈없이 그냥 같은 인덱스에 추가
    return a

 

 

 

제한시간은 30분이지만 거진 1시간은 걸린 것 같다. 그래도 이렇게 코드를 전시하는 이유는 너어어어무 뿌듯하기 때문이다!! 물론 효율성은 떨어지는 코드다. reversed() 함수를 사용해도 됐지만 다른 로직으로 구현하고 싶은 욕심이 들어서 효율은 우선 재끼고 결국 만들어냈다!

알고리즘 공부를 제대로 시작한지는 이틀밖에 안됐는데 스스로가 만족스러운 성과를 냈다!! 야호!

 

계산기 만들기

 

 

import tkinter as tk

operation = ''   #연산자 저장 변수
temp_number = 0   #이전값 저장 변수

def button(val):
    if val == 'C':
        number.delete(0, 'end')
    else:
        number.insert('end', val)
        print(val)
    

def cal(val):
    global operation
    global temp_number
    if number.get() != '':
        operation = val
        temp_number = int(number.get())
        number.delete(0, 'end')
        print(temp_number, operation) 
    
def equl():
    global operation
    global temp_number
    if operation != '' and number.get() !='':
        num = int(number.get())
        if operation == '+':
            solution = temp_number+num
        elif operation == '-':
            solution = temp_number - num
        elif operation == '*':
            solution = temp_number * num
        else:
            solution = temp_number/num
            
        number.delete(0,'end')
        number.insert(0,solution)            
        print(temp_number,operation,num,'=',solution) 
        operation =''
        temp_number = 0
    
    
win = tk.Tk()
win.title('Calculator')
win.geometry('300x400+100+100')
win.resizable(False, False)

label = tk.Label(win, height=4)
label.grid(row=0, column=0)

entry_value = StringVar(win, value='')

number = ttk.Entry(win, textvariable = entry_value, width=20)  #숫자 및 결과 표시창
number.grid(row=0, columnspan=3)

b1 = tk.Button(text='1', width=7, height=4, command=lambda:button('1'))
b1.grid(row=1, column=0)
b2 = tk.Button(text='2', width=7, height=4, command=lambda:button('2'))
b2.grid(row=1, column=1)
b3 = tk.Button(text='3', width=7, height=4, command=lambda:button('3'))
b3.grid(row=1, column=2)
b4 = tk.Button(text='+', width=7, height=4, command=lambda:cal('+'))
b4.grid(row=1, column=3)

b5 = tk.Button(text='4', width=7, height=4, command=lambda:button('4'))
b5.grid(row=2, column=0)
b6 = tk.Button(text='5', width=7, height=4, command=lambda:button('5'))
b6.grid(row=2, column=1)
b7 = tk.Button(text='6', width=7, height=4, command=lambda:button('6'))
b7.grid(row=2, column=2)
b8 = tk.Button(text='-', width=7, height=4, command=lambda:cal('-'))
b8.grid(row=2, column=3)

b9 = tk.Button(text='7', width=7, height=4, command=lambda:button('7'))
b9.grid(row=3, column=0)
b10 = tk.Button(text='8', width=7, height=4, command=lambda:button('8'))
b10.grid(row=3, column=1)
b11 = tk.Button(text='9', width=7, height=4, command=lambda:button('9'))
b11.grid(row=3, column=2)
b12 = tk.Button(text='*', width=7, height=4, command=lambda:cal('*'))
b12.grid(row=3, column=3)

b13 = tk.Button(text='0', width=7, height=4, command=lambda:button('0'))
b13.grid(row=4, column=0)
b14 = tk.Button(text='C', width=7, height=4, command=lambda:f1('C'))
b14.grid(row=4, column=1)
b15 = tk.Button(text='=', width=7, height=4, command=lambda:equl())
b15.grid(row=4, column=2)
b16 = tk.Button(text='/', width=7, height=4, command=lambda:cal('/'))
b16.grid(row=4, column=3)
    
win.mainloop()

아직 계산 오류(소숫점 등)를 고려하지 않은 상태이다.

 

앗, eval 함수를 쓰면 cal, equl 함수 없이도 되는구나..

def f1(val):
    s = label.cget('text')
    s += val #s:12
    label.config(text=s)#config():위젯 속성의 값을 읽거나 설정
    
def calc():
    s = label.cget('text') #s:'5-1+6*2'
    print(s)
    res = eval(s)
    label.config(text=str(res))
    
def clear():
    label.config(text='')
    
win = tk.Tk()#Tk객체 생성=>기본 윈도우와 ui api를 제공하는 객체
win.title('window title')
win.geometry('300x400+100+100')
win.resizable(False, False)

#위젯 생성 및 배치
label = tk.Label(win, height=4)
label.grid(row=0, column=0)

b1 = tk.Button(text='1', width=7, height=4, command=lambda:f1('1'))
b1.grid(row=1, column=0)
b2 = tk.Button(text='2', width=7, height=4, command=lambda:f1('2'))
b2.grid(row=1, column=1)
b3 = tk.Button(text='3', width=7, height=4, command=lambda:f1('3'))
b3.grid(row=1, column=2)
b4 = tk.Button(text='+', width=7, height=4, command=lambda:f1('+'))
b4.grid(row=1, column=3)
b5 = tk.Button(text='4', width=7, height=4, command=lambda:f1('4'))
b5.grid(row=2, column=0)
b6 = tk.Button(text='5', width=7, height=4, command=lambda:f1('5'))
b6.grid(row=2, column=1)
b7 = tk.Button(text='6', width=7, height=4, command=lambda:f1('6'))
b7.grid(row=2, column=2)
b8 = tk.Button(text='-', width=7, height=4, command=lambda:f1('-'))
b8.grid(row=2, column=3)
b9 = tk.Button(text='7', width=7, height=4, command=lambda:f1('7'))
b9.grid(row=3, column=0)
b10 = tk.Button(text='8', width=7, height=4, command=lambda:f1('8'))
b10.grid(row=3, column=1)
b11 = tk.Button(text='9', width=7, height=4, command=lambda:f1('9'))
b11.grid(row=3, column=2)
b12 = tk.Button(text='*', width=7, height=4, command=lambda:f1('*'))
b12.grid(row=3, column=3)
b13 = tk.Button(text='0', width=7, height=4, command=lambda:f1('0'))
b13.grid(row=4, column=0)
b14 = tk.Button(text='C', width=7, height=4, command=clear)
b14.grid(row=4, column=1)
b15 = tk.Button(text='=', width=7, height=4, command=calc)
b15.grid(row=4, column=2)
b16 = tk.Button(text='/', width=7, height=4, command=lambda:f1('/'))
b16.grid(row=4, column=3)
    
win.mainloop()

 

 

심지어 버튼 구현 노가다 없이 for문으로도 가넝...

 

def f1(val):
    s = label.cget('text')
    s += val
    label.config(text=s)   #config():위젯 속성의 값을 읽거나 설정

def calc():
    s = label.cget('text')
    print(s)
    res = eval(s)    #eval 함수: 자동으로 계산해주는 함수
    label.config(text=str(res))
    
def clear():
    label.config(text='')
    
win = tk.Tk()
win.title('window title')
win.geometry('300x400+100+100')
win.resizable(False, False)
   
label = tk.Label(win, height=4)
label.grid(row=0, column=0)

nums = [['1','2','3','+'],['4','5','6','-'],['7','8','9','*'],['0','C','=','/']]

    
for i in range(0, 4):
    for j in range(0, 4):
        num = nums[i][j]
        if num =='C':
            b = tk.Button(text=num, width=7, height=4, command=clear)
        elif num == '=':
            b = tk.Button(text=num, width=7, height=4, command=calc)
        else:
            b = tk.Button(text=num, width=7, height=4, command=lambda val=num :f1(val))
        b.grid(row=(i+1), column=j)
        
        
win.mainloop()

 

'파이썬II' 카테고리의 다른 글

파이썬II #Web Data Read  (0) 2021.07.02
파이썬II # 공공데이터 분석(1)  (0) 2021.07.01
파이썬II #파일 open 후 데이터 추출하기  (0) 2021.06.30

 

import  member.memModel as mem

class memMenu:
    def __init__(self):
        self.memService = mem.MemService()

    def memRun(self):
        flag = True
        while flag:
            m = input('1.회원가입 2.로그인 3.내정보확인 4.내정보수정 5.로그아웃 6.탈퇴 7.메뉴나감')
            if m=='1':
                self.memService.join()
            elif m=='2':
                self.memService.login()
            elif m=='3':
                self.memService.printMyInfo()
            elif m=='4':
                self.memService.editMyInfo()
            elif m=='5':
                self.memService.logout()
            elif m=='6':
                self.memService.delMyInfo()
            elif m == '7':
                break


 

 

Main
import Menu

def main():
    m = Menu.memMenu()
    m.memRun()

main()

 

 

접근제어 Private

 

접근제어는 클래스의 멤버변수나 메소드를 외부 접근을 허용할지 여부를 지정해준다.
객체지향 기반 프로그래밍에서는 접근 제어자 즉, private, protected, public을 이용한 정보 은닉을 통해 외부로부터 데이터를 보호한다. 이를 통해 객체의 속성과 메소드를 하나로 묶고, 실제 구현 내용의 일부는 외부에 감추어 은닉할 수 있는 캡슐화가 가능한다.

일반 멤버 변수나 메소드는 클래스 밖에서도 사용가능한데 이런 것들이 public이고, 클래스 밖에서는 사용이 불가능하게 하는 것이 private이다.

private 접근제어자는 이름앞에 __를 붙여서 사용한다.

 

class Test:
    def __init__(self):
        self.__a = 10
        self.b = 20

    def setData(self,a, b):
        self.__a = a
        self.b = b

    def printData(self):
        print('a:', self.__a)
        print('b:', self.b)

def main():
    t1 = Test()
    t1.printData()
    t1.setData(3,4)
    t1.printData()

    t1.b = 200

    print('t1.a:', t1.__a)
    print('t2.b:', t1.b)

main()

private 멤버 때문에 에러가 났다. 

 

 

class Test:
    def __init__(self):
        self.__a = 10
        self.b = 20

    def setData(self,a, b):
        self.__a = a
        self.b = b

    def printData(self):
        print('a:', self.__a)
        print('b:', self.b)

    def setA(self, a):  #setter: 클래스 밖에서 private 멤버에 값을 설정하는 메소드
       self.__a = a

    def getA(self):  #getter:클래스 밖으로 private 멤버 값을 반환하여 외부에서도 읽을 수 있게 하는 메소드
        return self.__a

def main():
    t1 = Test()
    t1.printData()
    t1.setData(3,4)
    t1.printData()

    t1.setA(100)
    t1.b = 200

    print('t1.a:', t1.getA())
    print('t2.b:', t1.b)

main()

짜잔. 이제는 a가 보이지롱.

setter와 getter을 이용하여 프라이빗한 a를 찾아내고 받아와서 메소드에 적용시켜주면 숨어있던 변수들도 꺼내 쓸 수 있다.

 

 

예제2)

class Member:
    def __init__(self, id='', pwd='', email=''):
        self.__id = id
        self.__pwd = pwd
        self.__email = email

    def __str__(self):   #객체를 설명하는 메소드
        return 'id:'+self.__id+'/ pwd:'+self.__pwd+'/ email:'+self.__email

    def setA(self, id):
        self.__id = id

    def getA(self):
        return self.__id


def main():
    m1 = Member('aaa', '111', 'aaa@email.com')
    m2 = Member('bbb', '222', 'bbb@email.com')
    m3 = Member('ccc', '333', 'ccc@email.com')
    print(m1)
    print(m2)
    print(m3)


    m1.setA('aaa1')
    m2.setA('bbb2')
    m3.setA('ccc3')
    print(m1)
    print(m2)
    print(m3)

main()

 

class PocketMon():  #상속이 목적. 객체생성 안함
    def __init__(self):
        self.name = ''
        self.hp = 0
        self.exp = 0
        self.lv = 1

    #상속하기 위한 메서드. 간단한 출력문만 작성. 실제 동작은 각 캐릭터의 하위 클래스에서
    def 밥먹기(self):
        print(self.name, '이(가) 밥을 먹는다.')

    def 잠자기(self):
        print(self.name, '이(가) 잠을 잔다.')

    def 운동하기(self):
        print(self.name, '이(가) 운동한다.')

    def 놀기(self):
        print(self.name, '이(가) 논다.')

    def 레벨체크(self):
        print(self.name, '레벨체크')

    #모든 캐릭터가 동일하게 동작하므로 부모 클래스에 구현한 것을 하위에서 그대로 사용
    def 상태정보(self):
        print(self.name, '상태정보 출력')
        print('HP:', self.hp)
        print('EXP:', self.exp)
        print('LV:', self.lv)


class 피카츄(PocketMon):
    def __init__(self):
        super().__init__()
        self.name = '피카츄'
        self.hp = 30   #캐릭터의 초기값 할당

    def 밥먹기(self):
        super().밥먹기()  #부모 클래스의 밥먹기()호출
        self.hp += 5

    def 잠자기(self):
        super().잠자기()
        self.hp += 10

    def 운동하기(self):
        super().운동하기()
        self.hp -= 10
        flag = self.hp>0   #살았나 죽었나 확인한 값 flag에 저장
        if flag:
            self.exp += 8
            self.레벨체크()
        return flag

    def 놀기(self):
        super().놀기()
        self.hp -= 5
        flag = self.hp > 0
        if flag:
            self.exp += 3
            self.레벨체크()
        return flag

    def 레벨체크(self):
        if self.exp>=20:
            self.lv += 1
            print(self.name, '레벨업!')
            self. exp -= 20

    def 전기공격(self):
        print('피카츄 백만볼트~~~~~! 피카피카!!!')

class 꼬부기(PocketMon):
    def __init__(self):
        super().__init__()
        self.name = '꼬부기'
        self.hp = 40

    def 밥먹기(self):
        super().밥먹기()
        self.hp += 7

    def 잠자기(self):
        super().잠자기()
        self.hp += 12

    def 운동하기(self):
        super().운동하기()
        self.hp -= 15
        flag = self.hp>0
        if flag:
            self.exp += 10
            self.레벨체크()
        return flag

    def 놀기(self):
        super().놀기()
        self.hp -= 8
        flag = self.hp > 0
        if flag:
            self.exp += 3
            self.레벨체크()
        return flag

    def 레벨체크(self):
        if self.exp>=25:
            self.lv += 1
            print(self.name, '레벨업!')
            self. exp -= 25

    def 물대포(self):
        print('물대포 공격!! 꼬북~~!!!!')


class 파이리(PocketMon):
    def __init__(self):
        super().__init__()
        self.name = '파이리'
        self.hp = 60

    def 밥먹기(self):
        super().밥먹기()
        self.hp += 12

    def 잠자기(self):
        super().잠자기()
        self.hp += 22

    def 운동하기(self):
        super().운동하기()
        self.hp -= 20
        flag = self.hp>0
        if flag:
            self.exp += 15
            self.레벨체크()
        return flag

    def 놀기(self):
        super().놀기()
        self.hp -= 10
        flag = self.hp > 0
        if flag:
            self.exp += 7
            self.레벨체크()
        return flag

    def 레벨체크(self):
        if self.exp>=30:
            self.lv += 1
            print(self.name, '레벨업!')
            self. exp -= 30

    def 불대포(self):
        print('파이리 불대포 공격!! (하지만 파이리는 말을 듣지 않았다.)')

class Menu:
    def __init__(self, mon):
        self.mon = mon

    def run(self):
        flag = True
        while flag:
            menu = int(input('1.밥먹기 2.잠자기 3.운동하기 4.놀기 5.상태확인 6.특기공격 7.종료'))
            if menu == 1:
                self.mon.밥먹기()
            elif menu == 2:
                self.mon.잠자기()
            elif menu == 3:
                flag = self.mon.운동하기()
            elif menu == 4:
                flag = self.mon.놀기()
            elif menu == 5:
                self.mon.상태정보()
            elif menu == 6: #isinstance(obj, class name) obj 객체가 class name으로 만들어진 객체냐?(T/F)
                if isinstance(self.mon, 피카츄):   #서로 일치하지 않는 메소드를 이용하고 싶을 때
                    self.mon.전기공격()
                elif isinstance(self.mon, 꼬부기):
                    self.mon.물대포()
                elif isinstance(self.mon, 파이리):
                    self.mon.불대포()

            elif menu == 7:
                flag = False

        if menu == '4' or menu == '3':
            print('캐릭터 사망')

        print('게임종료')

def main():
    print('포켓몬 게임 시작')
    m = input('캐릭터 선택\n1.피카츄(기본) 2.꼬부기 3.파이리')
    mon = None
    if m == '1':
        mon = 피카츄()
    if m == '2':
        mon = 꼬부기()
    elif m == '3':
        mon = 파이리()

    mon.상태정보()
    mm = Menu(mon)
    mm.run()
main()

 

 

점점 class와 def를 쓰는 것이 익숙해지고 있다. 리스트나 for문을 써서 머리를 싸매는 것이 더 까탈스러움을 느낀다. 조만간 미니 팀프로젝트가 시작되는데 처음에는 무지 상태에서 프로젝트를 시작하는 것이 두려웠으나 가능성이 살짝 보인다. 한 5퍼 정도?

곧 DB가 시작되는데 괜찮겠지..?

상속(inheritance)

 

상속이란? 말 그대로 유전이다. 물려줌과 물려받는 것.

부모 객체의 변수나 메소드를 물려주고 자식 객체가 물려받아 활용하는 것.

 

상속은 
1. 코드 재사용을 높이기 위해서
2. 다형성 구현을 위해

3. 조립(인터페이스)의 편리를 위해

사용하게 된다. 

 

class 부모클래스:
    ...내용...

class 자식클래스(부모클래스):
    ...내용...

도출된 객체 중 비슷한 객체들이 발견되면 공통된 코드를 추출하여 부모 클래스로 정의하면 자식 클래스들은 비슷한 클래스들을 상속받아서 정의하면 된다.

 

 

자식클래스들은 꼭

def Child(Parent)
	super().__inint__()

이라고 정의해야지만 상속을 받을 수 있다.

 

예제1)

class Parent:
    def __init__(self):
        self.a=10 #멤버변수=한개
        print('Parent 생성자')

    def method1(self):
        print('메서드1')

class Child(Parent): #패런트 클래스를 상속받은 차일드 클래스. 부모의 멤버변수와 메서드를 물려받는다.
    def __init__(self):
        super().__init__()    #super():부모객체 반환
        print('Child 생성자')
        self.b=20

    def method2(self):   #메소드 method1, method2 2개
        print('메소드2')

class Child2(Parent):
    def __init__(self):
        super().__init__()
        print('Child 생성자')
        self.c=20
        self.d=40

    def method3(self):   #메소드 method1, method2 2개
        print('메소드3')

def main():
    c1 = Child()
    print('c1.a: ', c1.a)
    print('c1.b: ', c1.b)

    c1.method1()
    c1.method2()


    c2 = Child2()
    print('c2.a: ', c2.a)
    print('c2.c: ', c2.c)
    print('c2.d: ', c2.d)

    c2.method1()
    c2.method3()

main()

 

 

 

예제2) 

class Person:   #번호,이름,정보출력은 모두 다 공통이라 부모클래스로
    def __init__(self):
        self.number = 0
        self.name = ''
        self.dept = ''

    def printInfo(self):
        print('number:', self.number)
        print('name:', self.name)
        print('dept:', self.dept)

class Student(Person):
    def __init__(self,number, name, dept):
        super().__init__()
        self.number=number
        self.name=name
        self.dept=dept
        self.type='학생'
        self.subj=[]

    def 수강신청(self,sub):
        self.subj.append(sub)

    def print수강과목(self):
        print('수강과목')
        for i in self.subj:
            print(i)

class Prof(Person):
    def __init__(self, number, name, dept):
        super().__init__()
        self.number = number
        self.name = name
        self.dept = dept
        self.type = '교수'
        self.담당과목 = []

    def 담당과목추가(self, sub):
        self.담당과목.append(sub)

    def print담당과목(self):
        print('수강과목')
        for i in self.담당과목:
            print(i)

class Staff(Person):
    def __init__(self, number, name, dept):
        super().__init__()
        self.number = number
        self.name = name
        self.dept = dept
        self.type = '교직원'
        self.job = ''

    def setJob(self, job):
        self.job = job

    def printJob(self):
        print('담당직무:', self.job)


def main():
    s1= Student(1, 'aaa','컴퓨터공학')
    s1.수강신청('전산학개론')
    s1.수강신청('컴퓨터비전')
    s1.printInfo()
    s1.print수강과목()

    p1=Prof(2, 'bbb', '전자공학')
    p1.담당과목추가('c언어')
    p1.담당과목추가('VHDL')
    p1.printInfo()
    p1.print담당과목()

    s2=Staff(3,'ccc','HR')
    s2.setJob('인재양성')
    s2.printInfo()
    s2.printJob()

main()

 

 

 

 

오버라이딩(Overriding)

 

오버라이딩이란 부모로부터 상속받은 메소드를 현재 클래스에 맞게 수정하여 사용하는 것이다. 즉, 메소드의 재정의이다.

#메소드 재정의: 부모로부터 상속받은 매소드를 현재 클래스에 맞게 수정하여 사용.

class Point2D:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def printPoint(self):
        print('x:', self.x, 'y:', self.y,end='')


class Point3D(Point2D):
    def __init__(self,x,y,z):
        super().__init__(x,y)
        self.z = z

    def printPoint(self):  #메소드 재정의
        super().printPoint()   #super().메소드 : 재정의한 메소드의 옛버전을 사용하고 싶을 때
        print(' z:', self.z)


def main():
    p1 = Point2D(1,2)
    p1.printPoint()
    print()

    p2 = Point3D(4,5,6)
    p2.printPoint()


main()

 

 

 

다형성(Polymorphism)

그럼 위에서 상속은 다형성 구현을 위해 사용한다고 했는데 다형성이 뭘까?

다형성은 같은 모양의 코드가 다른 동작을 하는 것이다. 다형성은 여러 객체를 하나의 타입으로 관리가 가능하기때문에 코드를 짧게 작성하는데 유리하다. 위에서 다룬 오버라이딩도 다형성의 한 예이다.

class Car:
    def __init__(self):
        self.name = ''

    def horn(self):
        print('자동차 빵빵')

class 소방차(Car):
    def __init__(self):
        super().__init__()
        self.name = '소방차'

    def horn(self):
        print('불꺼~~~~~~')


class 구급차(Car):
    def __init__(self):
        super().__init__()
        self.name = '구급차'

    def horn(self):
        print('삐오삐오')


class 경찰차(Car):
    def __init__(self):
        super().__init__()
        self.name = '경찰차'

    def horn(self):
        print('잡어~~~~~~')

def main():
    obj = None
    print('자동차를 선택하시오')
    s = input('1.소방차 2.구급차 3.경찰차')
    if s=='1':
        obj = 소방차()
    elif s == '2':
        obj = 구급차()
    elif s == '3':
        obj = 경찰차()
    else:
        ojb = 소방차()

    print(obj.name)
    obj.horn()

main()

차의 종류만 다를 뿐, 이름을 출력하고 경적소리를 프린트하는 것이 다 공통인 메소드이기 때문에 객체마다 메소드를 쓰는 것보다 위처럼 다형성을 이용해 작성하면 매우 간단해진다.

+ Recent posts