Main
import AppWindow as a
import tkinter as tk

def main():
    win = tk.Tk()   #윈도우창
    app = a.AppWin(win, '300x300+100+100')
    win.mainloop()

main()

 

 

 

AppWindow
import tkinter as tk
import bus_info.service as binfo

class AppWin(tk.Frame):#AppWin은 Frame(위젯을 배치하는 판)
    def __init__(self, root=None, geo=None): #root:Tk()객체 geo=창크기위치
        #fr = tk.Frame(self.root)
        super().__init__(root)  # 부모 생성자에 기본 윈도우 전달
        self.root = root  # 기본 윈도우를 멤버변수로 저장
        self.root.geometry(geo)  # 윈도우의 크기 및 위치 설정
        self.root.resizable(True, True)  # 윈도우의 가로, 세로 크기 재조정 가능으로 설정
        self.pack()
        self.service = binfo.BusService()  #버스 앱 기능이 구현된 서비스 객체 생성
        self.create_widgets()

    def create_widgets(self):  # 원하는 구성요소 부착
        self.title = tk.Label(self, text='버스 정보 앱')
        self.title.pack()
        self.inputData = tk.Entry(self, width=20)
        self.inputData.pack()
        self.btn1 = tk.Button(self, text='노선ID로검색', command=self.handle1) #command:이벤트 핸들러 등록 속성
        self.btn1.pack()
        self.btn2 = tk.Button(self, text='노선ID로경로검색', command=self.handle2)  # command:이벤트 핸들러 등록 속성
        self.btn2.pack()
        self.btn3 = tk.Button(self, text='버스명검색', command=self.handle3)  # command:이벤트 핸들러 등록 속성
        self.btn3.pack()
        self.btn4 = tk.Button(self, text='노선ID로정거장검색', command=self.handle4)  # command:이벤트 핸들러 등록 속성
        self.btn4.pack()
        self.content = tk.Label(self, text='')
        self.content.pack()

    def handle1(self):
        busId = self.inputData.get()
        res = self.service.getRouteInfoItem(busId)
        self.content.config(text=res)
        self.inputData.delete(0, tk.END)

    def handle2(self):
        busId = self.inputData.get()
        res = ''
        res_list = self.service.getRoutePathList(busId)
        if str(type(res_list)) == "<class 'str'>":
            res = res_list
        else:
            for i in res_list:
                res += i.__str__()+'\n'  #no:1(x, y)
        self.content.config(text=res)
        self.inputData.delete(0, tk.END)

    def handle3(self):
        busName = self.inputData.get()
        res = ''
        res_list = self.service.getBusRouteList(busName)
        if str(type(res_list)) == "<class 'str'>":
            res = res_list
        else:
            for i in res_list:
                res += i.__str__() + '\n'  # no:1(x, y)
        self.content.config(text=res)
        self.inputData.delete(0, tk.END)

    def handle4(self):
        busId = self.inputData.get()
        res = ''
        res_list = self.service.getStaionsByRouteList(busId)
        if str(type(res_list)) == "<class 'str'>":
            res = res_list
        else:
            for i in res_list:
                res += i.__str__() + '\n'  # no:1(x, y)
        self.content.config(text=res)
        self.inputData.delete(0, tk.END)

 

 

 

 

 

 

Vo
#Value Object: 값을 담을 클래스

class BusVo:#버스 노선 정보 담을 클래스
    def __init__(self, busId=None,busNm=None,stStat=None,edStat=None,term=None,firstTm=None,lastTm=None,corpNm=None):
        self.busRouteId=busId
        self.busRouteNm=busNm
        self.stStationNm=stStat
        self.edStationNm=edStat
        self.term=term
        self.firstBusTm=firstTm
        self.lastBusTm=lastTm
        self.corpNm=corpNm

    def __str__(self):
        res = ''
        res += 'bus id: ' + self.busRouteId
        res += '\nbus name: ' + self.busRouteNm
        res += '\nstart station: ' + self.stStationNm
        res += '\nend station: ' + self.edStationNm
        res += '\nterm: ' + self.term
        res += '\nfirst bus time: ' + self.firstBusTm
        res += '\nlast bus time: ' + self.lastBusTm
        res += '\ncorp name: ' + self.corpNm
        return res

class PointVo:
    def __init__(self, no=None, gps_x=None, gps_y=None):
        self.no = no
        self.gps_x = gps_x
        self.gps_y = gps_y

    def __str__(self):
        return 'no: '+self.no+' ('+ self.gps_x + ' , '+ self.gps_y + ')'


class StationVo:
    def __init__(self, seq=None, stat=None, id=None):
        self.seq=seq
        self.stationNm=stat
        self.arsId=id

    def __str__(self):
        return 'seq: '+self.seq+' / station name: '+self.stationNm+ ' / ars id:'+self.arsId

 

 

 

 

SERVICE


getRouteInfoItem(노선ID): 리턴값은 BUS_VO 객체 한개 반환

  1) 노선기본정보항목조회(URL:getRouteInfoItem): 
=> 버스 노선ID를 입력받는다 
=> OPEN API에 URL(getRouteInfoItem)로 요청보냄 
=> 요청 응답(XML: 이 파일은 버스 정보가 담겨있음)을 받으면 파싱을 해서 BUS_VO에 저장할 값들만 추출 
=> 추출한 정보를 BUS_VO에 담는다
=> BUS_VO 리턴


getRoutePathList(노선ID): 경로 묶음. POINT_VO객체들을 리스트에 담아서 반환

   2) 노선경로목록조회(URL:getRoutePathList)
=> 버스 노선ID를 입력받는다 
=> OPEN API에 URL(getRoutePathList)로 요청보냄
=> 요청 응답(XML: 경유하는 정류소의 좌표정보가 담겨있음)을 받으면 파싱을 해서 POINT_VO에 저장할 값들만 추출
=> 추출한 정보를 POINT_VO에 담아서 리스트에 담는다(정류소 좌표가 여러개)
=>리스트 반환


getBusRouteList(버스명): 찾아진 버스정보를 BUS_VO 객체로 만들어서 리스트에 담아서 반환

 3) 노선번호목록조회(URL: getBusRouteList)
=> 버스명 입력받는다(100번 입력하면 이름에 100이 들어간 모든 버스 검색. 여러개 검색됨)
=> OPEN API에 URL(getBusRouteList)로 요청보냄
=> 요청 응답(XML: 이 파일은 버스 정보가 담겨있음)을 받으면 파싱을 해서 BUS_VO에 저장할 값들만 추출 
=> 추출한 정보를 BUS_VO에 담고 이를 리스트에 담는다(버스 여러개 검색됨)
=>리스트 반환


getStaionsByRouteList(노선ID): 찾아진 이 노선의 정거역을 STATION_VO 객체로 만들어 리스트에 담아서 반환

   4) 노선별경유정류소목록조회(URL: getStaionsByRouteList)
=> 버스 노선ID를 입력받는다 
=> OPEN API에 URL(getStaionsByRouteList)로 요청보냄
=> 요청 응답(XML: 경유하는 정류소의 다양한 정보가 담겨있음)을 받으면 파싱을 해서 STATION_VO에 저장할 값들만 추출
=> 추출한 정보를 STATION_VO에 담아서 리스트에 담는다(정류소 여러개가 검색됨)
=>리스트 반환

 

import requests
from bs4 import BeautifulSoup
import bus_info.vo as vo

class BusService:
    def __init__(self):
        self.url = 'http://ws.bus.go.kr/api/rest/busRouteInfo/%s?ServiceKey=%s&%s=%s'
        self.apiKey = 'BYgs6%2FjSL0du1z8yK4GxYdW1SepukkJ0gXtUP3tGUQpjThEU4JeQKRlspdSnxTWcjia6U6r5oPxW%2F7tK7HZ2sg%3D%3D'

    def getRouteInfoItem(self, busId):
        url = self.url%('getRouteInfo', self.apiKey, 'busRouteId', busId)
        print(url)
        html = requests.get(url).text #url 에 웹 요청
        root = BeautifulSoup(html, 'lxml-xml')  # 파서의 종류를 xml로 지정
        code = root.find('headerCd').get_text()
        msg = root.find('headerMsg').get_text()
        print('처리결과:', msg)
        if code == '0':
            busRouteId = root.find('busRouteId').get_text()
            busRouteNm = root.find('busRouteNm').get_text()
            stStationNm = root.find('stStationNm').get_text()
            edStationNm = root.find('edStationNm').get_text()
            term = root.find('term').get_text()
            firstBusTm = root.find('firstBusTm').get_text()
            lastBusTm = root.find('lastBusTm').get_text()
            corpNm = root.find('corpNm').get_text()

            return vo.BusVo(busRouteId, busRouteNm, stStationNm, edStationNm, term, firstBusTm, lastBusTm, corpNm)

    #노선id를 전달하면 노선의 경로를 (x,y)
    def getRoutePathList(self, busId):
        url = self.url % ('getRoutePath', self.apiKey, 'busRouteId', busId)
        print(url)
        html = requests.get(url).text  # url 에 웹 요청
        root = BeautifulSoup(html, 'lxml-xml')  # 파서의 종류를 xml로 지정
        code = root.find('headerCd').get_text()
        msg = root.find('headerMsg').get_text()
        print('처리결과:', msg)
        paths=[]
        if code == '0':
            itemList = root.select('itemList')
            for item in itemList:
                no = item.find('no').get_text()
                x = item.find('gpsX').get_text()
                y = item.find('gpsY').get_text()
                paths.append(vo.PointVo(no, x, y))

            return paths

    def getBusRouteList(self, busName):
        url = self.url % ('getBusRouteList', self.apiKey, 'strSrch', busName)
        print(url)
        html = requests.get(url).text  # url 에 웹 요청
        root = BeautifulSoup(html, 'lxml-xml')  # 파서의 종류를 xml로 지정
        code = root.find('headerCd').get_text()
        msg = root.find('headerMsg').get_text()
        print('처리결과:', msg)
        bus = []
        if code == '0':
            itemList = root.select('itemList')
            for item in itemList:
                busRouteId = item.find('busRouteId').get_text()
                busRouteNm = item.find('busRouteNm').get_text()
                stStationNm = item.find('stStationNm').get_text()
                edStationNm = item.find('edStationNm').get_text()
                term = item.find('term').get_text()
                firstBusTm = item.find('firstBusTm').get_text()
                lastBusTm = item.find('lastBusTm').get_text()
                corpNm = item.find('corpNm').get_text()

                bus.append(vo.BusVo(busRouteId, busRouteNm, stStationNm, edStationNm, term, firstBusTm, lastBusTm, corpNm))
            return bus

    def getStaionsByRouteList(self, busId):
        url = self.url % ('getStaionByRoute', self.apiKey, 'busRouteId', busId)
        print(url)
        html = requests.get(url).text  # url 에 웹 요청
        root = BeautifulSoup(html, 'lxml-xml')  # 파서의 종류를 xml로 지정
        code = root.find('headerCd').get_text()
        msg = root.find('headerMsg').get_text()
        print('처리결과:', msg)
        stations = []
        if code == '0':
            itemList = root.select('itemList')
            for item in itemList:
                seq = item.find('seq').get_text()
                stationNm = item.find('stationNm').get_text()
                arsId = item.find('arsId').get_text()
                stations.append(vo.StationVo(seq, stationNm, arsId))
            return stations

 

 

 

 

import bus_info.service as binfo

def main():
    bservice = binfo.BusService()#busservice 객체 생성

    vo = bservice.getRouteInfoItem('100100124')
    if vo == None:
        print('검색안됨')
    else:
        print(vo)


    '''
    bus = bservice.getBusRouteList('0017')
    if bus == None:
        print('없는 버스 이름')
    else:
        for b in bus:
            print(b)

    paths = bservice.getRoutePathList('100100124')
    if paths == None:
        print('없는 노선')
    else:
        for p in paths:
            print(p)
            
        stations = bservice.getStaionsByRouteList('100100124')
    if stations == None:
        print('없는 버스 이름')
    else:
        for s in stations:
            print(s)
    '''
main()

 

 

 

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()

 

 

import pymysql

#테이블값 저장
class MemVo:
    def __init__(self, id=None, pwd=None, name=None, email=None):
        self.id = id
        self.pwd = pwd
        self.name = name
        self.email = email

    def __str__(self):
        return 'id:'+self.id+' / pwd:'+self.pwd+' / name:'+self.name+' / email:'+self.email

class MemDao:#member 테이블과 관련된 db 작업만 구현
    def __init__(self):
        self.conn = None    #커넥션 객체 담을 멤버 변수

    #db연결함수. db 사용전 로그인하는 작업을 수행
    def connect(self):
        self.conn = pymysql.connect(host='localhost', user='root', password='1234', db='encore', charset='utf8')

    #db 닫는 함수
    def disconnect(self):
        self.conn.close()

    def insert(self, vo):#member 테이블에 한 줄 추가. 회원가입 정보를 저장
        self.connect() #db 연결
        cur = self.conn.cursor()  #사용할 커서 객체 생성
        #insert into member values(vo.id, vo.pwd, vo.name, vo.email)
        sql = "insert into member values(%s, %s, %s, %s)"   #변수가 들어갈 위치에 %s와 같은 포맷문자 지정
        vals = (vo.id, vo.pwd, vo.name, vo.email)
        cur.execute(sql, vals)
        self.conn.commit()
        self.disconnect()

    def select(self, id):
        self.connect()  # db 연결
        cur = self.conn.cursor()  # 사용할 커서 객체 생성
        sql = "select * from member where id=%s"  # 변수가 들어갈 위치에 %s와 같은 포맷문자 지정
        vals = (id,)
        cur.execute(sql, vals) #쿼리 실행. 검색 결과가 cur에 저장
        row = cur.fetchone()    #cur 객체에서 검색된 한 줄 fetch. 검색 결과 없으면 None반환
        self.disconnect()   #db 닫음
        if row!=None:   #검색된 결과가 있으면
            vo = MemVo(row[0], row[1], row[2], row[3])#id, pwd, name, email
            return vo

    def update(self, id, new_pwd):
        self.connect()  # db 연결
        cur = self.conn.cursor()  # 사용할 커서 객체 생성
        sql = "update member set pwd=%s where id=%s"  # 변수가 들어갈 위치에 %s와 같은 포맷문자 지정
        vals = (new_pwd, id)
        cur.execute(sql, vals)
        self.conn.commit()
        self.disconnect()

    def delete(self, id):
        self.connect()  # db 연결
        cur = self.conn.cursor()  # 사용할 커서 객체 생성
        sql = "delete from member where id=%s"  # 변수가 들어갈 위치에 %s와 같은 포맷문자 지정
        vals = (id,)
        cur.execute(sql, vals)
        self.conn.commit()
        self.disconnect()

class MemService:#제공할 기능 구현

    '''
    로그인 상태 유지 => 로그인함수에서 처리한 아이디를 어딘가 보관 =>
    로그인함수(): id, pwd를 입력받아서 db에 일치하는가 확인. 로그인 성공.
    기억상실처리 => MemService.login_id에 로그인 성공한 id를 저장. 로그아웃할때까지 유지
    MemService.login_id:None => 로그아웃 상태
    MemService.login_id:'aaa' => 'aaa'로 로그인 상태
    내정보확인 메뉴선택:
    '''

    login_id = None #로그인한 사람의 id보관. None이면 로그인 안된 상태

    def __init__(self):
        self.dao = MemDao() #대부분 기능이 db작업 필요. db작업 전담하는 dao객체를 멤버변수로 생성

    def join(self): #회원가입 기능
        print('회원가입')
        id = input('id:')
        pwd = input('pwd:')
        name = input('name:')
        email = input('email:')
        try:
            vo = MemVo(id, pwd, name, email)
            self.dao.insert(vo)#db에 id, pwd, name, email 저장
        except Exception as e:
            print(e)
        else:
            print('회원가입완료')

    def login(self):#로그인 기능
        print('로그인')
        if MemService.login_id != None: #로그인 상태
            print('이미 로그인 중')
            return
        id = input('id:')
        pwd = input('pwd:')
        vo = self.dao.select(id) #입력한 id로 검색
        if vo == None: #검색 결과 없음
            print('없는 아이디')
        else:
            if pwd == vo.pwd: #입력한 pwd와 vo에 담긴 pwd가 일치. 로그인 성공
                print('로그인 성공')
                MemService.login_id = id #로그인상태 유지
            else:
                print('패스워드 불일치')

    def logout(self):
        print('로그아웃')
        if MemService.login_id == None:#로그인 상태 확인
            print('로그인 먼저 해라')
            return
        MemService.login_id = None #로그아웃 상태

    def printMyInfo(self):#내정보확인기능
        print('내정보확인')
        if MemService.login_id == None:#로그인 상태 확인
            print('로그인 먼저 해라')
            return
        vo = self.dao.select(MemService.login_id)#로그인한 사람의 id를 select(id)에 담아 호출하면 로그인한 id정보 검색
        print(vo)

    def editMyInfo(self):#MemService.login_id로 새 pwd입력받아서 수정
        print('내정보수정')
        if MemService.login_id == None:#로그인 상태 확인
            print('로그인 먼저 해라')
            return
        new_pwd = input('새 pwd:')
        self.dao.update(MemService.login_id, new_pwd)

    def delMyInfo(self):#MemService.login_id로 삭제
        print('탈퇴')
        if MemService.login_id == None:
            print('로그인 먼저 해라')
            return

        self.dao.delete(MemService.login_id)#db에서 id가 동일한 행 삭제
        MemService.login_id = None  #로그아웃 처리
import pymysql

def select():
    conn, cur = None, None
    conn = pymysql.connect(host='127.0.0.1', user = 'root', password='1234',db = 'encore', charset='utf8')
    cur = conn.cursor()
    sql = 'select * from departments'
    cur.execute(sql)
    for row in cur:   #커서 객체에서 검색결과 한 줄씩 fetch
        print('dept_id:',row[0],'/ dept_name:', row[1], ' / manager_id:', row[2], ' / location_id:', row[3])

    conn.close()

def insert():
    conn, cur = None, None
    conn = pymysql.connect(host='127.0.0.1', user='root', password='1234', db='encore', charset='utf8')
    cur = conn.cursor()
    sql = 'insert into departments values(280, "dept test1", null, 1700)'
    cur.execute(sql)
    conn.commit()
    conn.close()

def update():
    conn, cur = None, None
    conn = pymysql.connect(host='127.0.0.1', user='root', password='1234', db='encore', charset='utf8')
    cur = conn.cursor()
    sql = 'update departments set department_name="가나다라", manager_id=100 where department_id=280'
    cur.execute(sql)
    conn.commit()
    conn.close()

def selectById(id):
    conn, cur = None, None
    conn = pymysql.connect(host='127.0.0.1', user='root', password='1234', db='encore', charset='utf8')
    cur = conn.cursor()
    sql = 'select * from departments where department_id='+str(id)
    cur.execute(sql)
    row = cur.fetchone()   #한줄 fetch
    if row == None:
        print('없는 부서 번호')
    else:
        print('dept_id:',row[0],'/ dept_name:', row[1], ' / manager_id:', row[2], ' / location_id:', row[3])
    conn.commit()
    conn.close()

def delete():
    conn, cur = None, None
    conn = pymysql.connect(host='127.0.0.1', user='root', password='1234', db='encore', charset='utf8')
    cur = conn.cursor()
    sql = 'delete from departments where department_id=280'
    cur.execute(sql)
    conn.commit()
    conn.close()

 

def main():
    insert()
    select()
    selectById(280)
    update()
    selectById(290)
    
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()

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

Q. 제품 등록, 수정, 삭제 등 제품관리 프로그램을 만드시오. (단, 제품번호는 자동할당 받아야 됌)

제품정보에는 제품번호, 제품명, 가격, 수량

메뉴에는 1.등록 2.검색 3.수정 4.삭제 5.전체출력 6.종료

 

#VO

class Product:
    cnt = 0
    def __init__(self, name='', price=0, amount=0):
        Product.cnt += 1
        self.num = Product.cnt
        self.name = name
        self.price = price
        self.amount = amount

    def __str__(self):
        return '제품번호:' + str(self.num) + '/ 제품명:' + self.name + '/ 가격:' + str(self.price) + '/ 수량:' + str(self.amount)
        print()

#DAO
class ProdDao:
    def __init__(self):
        self.datas = []

    def insert(self, p):
        self.datas.append(p)

    def delete(self, p):
        self.datas.remove(p)

    def select(self, num):
        for i in self.datas:
            if i.num == num:
                return i

    def selectAll(self):
        return self.datas


#Service
class ProdService:
    def __init__(self):
        self.dao = ProdDao()

    def addProd(self):
        name = input('제품명:')
        price = input('가격:')
        amount = input('수량:')

        p = Product(name, price, amount)
        self.dao.insert(p)

    def printProd(self):
        num = int(input('검색할 제품번호를 입력하시오.'))
        p = self.dao.select(num)
        if p == None:
            print('제품을 찾을 수 없습니다.')
        else:
            print(p)

    def editProd(self):
        num = int(input('검색할 제품번호를 입력하시오.:'))
        pd = self.dao.select(num)
        if pd == None:
            print('제품을 찾을 수 없습니다.')
        else:
            pd.price=int(input('수정할 가격을 입력하시오.'))
            pd.amount=int(input('수정할 수량을 입력하시오.'))
            print('수정되었습니다.')

    def delProd(self):
        num = int(input('검색할 제품번호를 입력하시오.:'))
        pd = self.dao.select(num)
        if pd == None:
            print('제품을 찾을 수 없습니다.')
        else:
            self.dao.delete(pd)
            print('삭제되었습니다.')

    def printAll(self):
        datas = self.dao.selectAll()
        for i in datas:
            print(i)

class Menu:
    def __init__(self):
        self.service = ProdService()

    def run(self):
        while True:
            menu = int(input('1.등록 2.검색 3.수정 4.삭제 5.전체출력 6.종료'))
            if menu == 1:
                self.service.addProd()
            if menu == 2:
                self.service.printProd()
            if menu == 3:
                self.service.editProd()
            if menu == 4:
                self.service.delProd()
            if menu == 5:
                self.service.printAll()
            if menu == 6:
                break

def main():
    pd=Menu()
    pd.run()

main()

야호! 간단한 프로그램이지만 뭔가를 만들어 내는 것에 익숙해지고 있다.

맨 처음 오류는 변수 num이 int타입인데 서비스 클래스에서 printProd 메소드에서의 검색할 num을 그냥 str로 입력받아 오류가 났었다.

그리고 두번째는 Attributeerror: 'Product' object has no attribute 'printprod'가 떴었다.

처음에는 Product 클래스에서 출력메소드명을 print라고 했었는데 이게 printProd와 서로 같지 않아서 생긴 오류였다.

서로 다른 클래스 내에 속해있지만 같은 함수명을 가져야 했구나... 거의 2시간동안 오류가 난 것이 죄다 printProd 메소드때문이어서 당분간 쟤랑 좀 절교해야겠다.

 

Heap, Stack, Static

힙(heap)은 malloc(크기), new, 생성자 호출로 메모리를 할당받는다.

주소로 접근해야 하기 때문문에 변수가 필요하고 리스트나 객체가 저장되는 메모리이다.

스택(stack)은 함수나 메소드 호출되면 할당받는 메모리이다. 변수선언으로 할당받기 때문에 변수명이 필요하고 

한번 호출되고 나면 사라지는 성질이 있다(유효시간이 가장 짧음).

static은 클래스변수를 저장한다. 프래그램이 시작해서부터 종료할때까지 존재한다.

RAM
static
heap
stack

이런 순으로 RAM에 위치하고 있다.

 

class Test:
    x=0   #클래스 변수. 정적 멤버 변수. 정적 메모리(static)에 저장.
            #이 클래스로 만든 모든 객체가 공용으로 사용
    def __init__(self):
        self.y = 0     #일반멤버변수. 앞에 self.를 꼭 붙인다. 이 멤버변수는 self객체 소속

    def printXY(self):
        print('x:',Test.x)
        print('y:',self.y)

def main():
    print(Test.x)   #객체를 생성하지 않아도 사용가능
    #print(t1.y)    #일반멤버변수는 객체 사용 전에 사용 불가


    t1= Test()
    Test.x+=1
    t1.y+=1
    t1.printXY()

    t2= Test()
    Test.x+=1
    t2.y+=1
    t2.printXY()

    t3= Test()
    Test.x+=1
    t3.y+=1
    t3.printXY()

main()

 

파이썬에서는 객체를 통하지 않고 사용할 수 있는 변수를 생성하는 것이 가능하다.

이들 변수는 모든 객체를 통틀어서 하나만 생성되고 모든 객체가 이것을 공유하게 되는데 이러한 변수를 정적 멤버변수

또는 클래스 멤버(class member)라고 한다. 

간단하게는 변수 앞에 self.가 없다면 정적변수라고 할 수 있다.

 

출력에서 보다시피 정적변수들은 모든 객체에서 공유하기 때문에 정적변수 x의 값이 점점 누적되어 하나씩 증가한다.

y는 동적변수이기 때문에 처음 초기화 해준 값인 0으로 되돌아간다.

 

 

class Test:
    cnt = 0     #객체들이 공유해야 하는 값
    def __init__(self):
        Test.cnt += 1
        print(Test.cnt,'번째 객체 생성')



def main():
    for i in range(0, 5):
        Test()

main()

 

위 예제에서 볼수 있듯 cnt가 정적변수이기 때문에 계속 누적이 되어 print 되는 것을 알수있다.

 

 

 

 

정적메소드

정적메소드는 인스턴스를 생성하고 호출하는 방식이 아니라서 클래스에서 바로 호출이 가능하다.

클래스안의 메소드이지만 self가 없지만 클래스 소속이기 때문에 클래스 이름으로 사용가능!

 

 

엥 여기서 잠깐!

객체 = 클래스 + 인스턴스

인스턴스(객체 지향 프로그래밍(OOP)에서 해당 클래스의 구조로 컴퓨터 저장공간에서 할당된 실체를 의미)

메소드 : 클래스 내부에서 정의된 함수 (객체의 함수)

 

class Test:
    x = 0   #클래스 변수. 정적 멤버 변수

    def __init__(self):
        self.y = 0

    def method1(self):   #일반메소드. 클래스변수, 멤버변수 모두 사용가능
        print('method1: 멤버메소드')
        print('x:',Test.x)
        print('y:',self.y)

    def method2():   #정적메소드. 객체 생성전에 사용가능하므로 일반멤버변수 사용 불가.
        print('method2: 정적메소드')
        print('x:',Test.x)
        name = 'aaa'
        print(name)
        #print('y:',self.y)

    def method3(self):   #일반메소드는 일반 메서드 호출도 가능. 정적메소드 호출도 가능.
        print('method3: 멤버메소드')
        self.method1()   #일반 메소드호출
        Test.method2()   #정적 메소드호출

    def method4():   #정적메소드. 객체 생성전이므로 일반 메소드 호출 불가
        print('method4: 정적메소드')
        #self.method1()
        Test.method2()

def main():
    Test.method2()
    Test.method4()

    t1=Test()
    t1.method1()
    t1.method3()

main()

 

정적 메서드는 self를 받지 않으므로 인스턴스 속성에는 접근할 수 없다. 그래서 보통 정적 메소드는 인스턴스 속성, 인스턴스 메소드가 필요 없을 때 사용한다.

그럼 무엇을 정적 메서드로 만들어야 할까? 정적 메소드는 메소드의 실행이 외부 상태에 영향을 끼치지 않는 순수 함수(pure function)를 만들 때 사용한다.

순수 함수는 부수 효과(side effect)가 없고 입력 값이 같으면 언제나 같은 출력 값을 반환한다(약간..상수함수 느낌?)

즉, 정적 메소드는 인스턴스의 상태를 변화시키지 않는 메소드를 만들 때 사용합니다.

 

import math


class Math:
    #파이썬에서 상수 정의 방법

    __pi = 3.14   #변수앞에 __붙이면 private멤버변수(클래스 밖에서 안보임)
    PI = __pi

    def circle(r):
        return r*r*Math.PI

    def rect(w, h):
        return w*h

def main():
    print('pi:', Math.PI)
    w = Math.circle(5)
    print('원의 넓이:',w)

    w = Math.rect(5, 10)
    print('사각형의 넓이:', w)

main()

정적메소드는 이렇게 수학적인 연산이나 계산쪽에서 활용도가 높을 것 같다.

+ Recent posts