
object-oriented programming(OOP)
프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체(object)'라는 기본 단위로 나누고 이들의 상호작용으로 서술하는 방식이다. 객체란 하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음으로 봐야 한다.
큰 문제를 작게 쪼개는 것이 아니라, 먼저 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-up) 해결법을 도입한 것이다.
이 객체란 것을 일단 한번 독립성/신뢰성이 높게 만들어 놓기만 하면 그 이후엔 그 객체를 수정 없이 재사용할 수 있으므로 개발 기간과 비용이 대폭 줄어들게 된다.(출처: 나무위키)

class Robot: def __init__(self,color,height,weight): self.color = color self.height = height self.weight = weight def printRobotInfor(self): print(f'color: {self.color}') print(f'height: {self.height}') print(f'weight: {self.weight}') rb1 = Robot('red',200,80) rb2 = Robot('blue',300,90)
위 예제를 살펴보면,
클래스 생성자로부터 Robot 이라는 클래스를 생성하였고 객체를 두개 생성하였다.
중요한것은 이 두 객체를 참조하는 rb1과 rb2라는 변수는 메모리에 저장이 되어있는 것이아니라 메모리 바깥의 공간에 존재하며 객체의 '주소값'을 저장한 것 이다.
즉, 변수 rb1과 rb2는 객체의 메모리 주소를 저장하고 이를 이용해서 객체를 참조하고 있는 것이다. (아래 그림 참조)

위 예제에서 rb1과 rb2가 하나의 객체를 참조하게 하려면 다음과 같은 코드를 입력하면 된다.
rb2 = rb1 rb2.color = 'pink' print(rb1.color)
살펴보면 rb2의 색상을 핑크로 변경했는데 rb1의 색상을 출력했을 때 핑크로 나오는 것을 확인할 수 있다. 즉 두 변수가 하나의 객체를 참조하고 있다는 것을 알 수 있다.
그렇다면 하나의 객체를 참조하는 것이 아닌 객체 자체를 복사하여 두개의 다른 객체를 만들어보자
import copy rb2 = copy.copy(rb1) rb2.printRobotInfo() #실행결과 color: pink height: 200 weight: 80
copy 모듈의 copy 함수를 사용하여 rb1을 복사하여 새로운 rb2가 생성되었다. rb2를 출력해보면 색상을 위에서 변경한 핑크와 rb1의 height, weight을 그대로 받아 복사가 된 것을 확인 할 수 있다.
실습
➩국어,영어,수학 점수를 입력받아 리스트에 저장하고 원본을 유지한 상태로, 복사본을 만들어 과목별 점수를 10% 올렸을 경우에 평균을 출력해 보자.
scores = [int(input('국어 점수 입력:')), int(input('영어 점수 입력:')), int(input('수학 점수 입력:'))] print(scores) copyScores = scores.copy() for idx, score in enumerate(copyScores): result = score * 1.1 copyScores[idx] = 100 if result > 100 else result print(f'이전 평균: {sum(scores) / len(scores)}') print(f'이후 평균: {sum(copyScores) / len(copyScores)}') #실행결과 국어 점수 입력:80 영어 점수 입력:90 수학 점수 입력:100 [80, 90, 100] 이전 평균: 90.0 이후 평균: 95.66666666666667
copy() 함수 : 위 예제에서 copy() 함수는 copyScores 라는 변수를 통해 scores의 동일한 주소값을 할당 받는 것이 아니라 실제로 새로운 복사본을 생성하는 것이다. enumerate 함수 : 이터러블한 객체를 순회하며 각 요소에 순서를 부여하는데 사용되는 파이썬 내장 함수 입니다. 클래스도 다른 클래스에 그 기능을 상속할 수 있고 상속 받은 클래스는 상속받은 기능을 사용할 수 있다.
예제
➩덧셈, 뺄셈 기능이 있는 클래스를 만들고, 이를 상속하는 클래스를 만들어서 곱셈과 나눗셈 기능을 추가해보자.
class Calculator1: def plus(self, n1, n2): result = n1 + n2 return result def sub(self, n1, n2): result = n1 - n2 return result class Calculator2(Calculator1): def mul(self,n1, n2): result = n1 * n2 return result def div(self,n1, n2): result = n1 / n2 return result cal = Calculator2() print(cal.plus(10,2)) print(cal.sub(10,2)) print(cal.mul(10,2)) print(cal.div(10,2))
class Calculator2(Calculator1) 으로 Calculator1의 기능을 2가 상속받았다. print로 출력을 해보면 1의 기능을 모두 사용할 수 있는 것을 알 수 있다.
생성자란 클래스로부터 객체가 생성될 때 파이썬 인터프린터에 의해 자동으로 호출되는 특별한 메서드이다.
객체가 생성될 때 생성자를 호출하면 __init__() 메서드가 자동 호출되며, 메모리에 생성된 객체 공간에 데이터를 넣거나 초기화하는 목적으로 사용한다.
(초기화 기능이 있다고 우선 생각하자)
메서드라는 용어도 헷갈릴 수 있으니 정의하자면 메서드는 클래스 내에 정의된 함수를 메서드라고 부른다. 메서드의 첫 번째 인자는 항상 self 이어야한다고 기억해두자.
예제
class A: def __init__(self,num1A,num2A): print('A클래스 생성자 호출') self.num1A = num1A self.num2A = num2A def A_function(self,num1A,num2A): return num1A + num2A class B(A): def __init__(self,num1B,num2B): print('B클래스 생성자 호출') self.num1B = num1B self.num2B = num2B def B_function(self): return num1B + num2B cal = B(10,20) #실행결과 B클래스 생성자 호출
위의 코드를 보면 클래스B가 A의 기능을 상속받았는데도 실행결과가 'B클래스 생성자 호출'만 출력되는 것을 볼 수 있다.
이것은 B클래스의 init메서드를 호출했기때문에 그 속성도(num1B=num1B, num2B=num2B)초기화 되었지만 A클래스의 속성값은 초기화 되지 않았기 때문에 출력이 안되는 것이다.
하지만 우리는 A의 속성값은 출력이 안되지만 A의 기능은 출력이 가능하다는 것은 클래스 상속을 배우면서 이해하였다.
cal = B('','') print(cal.A_function(10,20)) #실행결과 B클래스 생성자 호출 30
속성값도 출력하는 방법은 당연히(?)있다. super()라는 함수를 사용하면 된다. super().__init__(num1B,num2B)를 위 예제에 다시 추가해보자.
class A: def __init__(self,num1A,num2A): print('A클래스 생성자 호출') self.num1A = num1A self.num2A = num2A def A_function(self,num1A,num2A): return num1A + num2A class B(A): def __init__(self,num1B,num2B): print('B클래스 생성자 호출') self.num1B = num1B self.num2B = num2B super().__init__(num1B,num2B) def B_function(self): return num1B + num2B cal = B('','') print(cal.A_function(10,20)) #실행결과 B클래스 생성자 호출 A클래스 생성자 호출 30
즉 정리해보면, 클래스 상속을 받으면 기능은 상속하여 사용할 수 있지만, 속성은 init메서드가 호출되어야지 속성이 초기화되면서 사용할 수 있다. (중요***)
실습
➩중간고사 클래스와 기말고사 클래스를 상속관계로 만들고 각각의 점수를 초기화 하자. 또한 총점 및 평균을 반환하는 기능도 만들어보자
class midExam: def __init__(self,a1,a2,a3): self.mid_korscore = a1 self.mid_engscore = a2 self.mid_matscore = a3 def printscores(self): print(f'중간고사 국어점수: {self.mid_korscore}') print(f'중간고사 영어점수: {self.mid_engscore}') print(f'중간고사 수학점수: {self.mid_matscore}') class finalExam(midExam): def __init__(self,a1,a2,a3,a4,a5,a6): super().__init__(a1,a2,a3) self.final_korscore = a4 self.final_engscore = a5 self.final_matscore = a6 def printscores(self): super().printscores() print(f'기말고사 국어점수: {self.final_korscore}') print(f'기말고사 영어점수: {self.final_engscore}') print(f'기말고사 수학점수: {self.final_matscore}') def getTotalScore(self): total = self.mid_korscore+self.mid_engscore+self.mid_matscore total += self.final_korscore+self.final_engscore+self.final_matscore return total def getAverageScore(self): return self.getTotalScore() / 6 exam1 = finalExam(90,80,100,70,80,100) exam1.printscores() print(f'총점 :{exam1.getTotalScore()}') print(f'평균: {round(exam1.getAverageScore(),2)}') #실행결과 중간고사 국어점수: 90 중간고사 영어점수: 80 중간고사 수학점수: 100 기말고사 국어점수: 70 기말고사 영어점수: 80 기말고사 수학점수: 100 총점 :520 평균: 86.67
상속과 동일한 개념으로 2개 이상의 클래스를 상속받는 경우이고
이경우도 형식은 동일하다.
그러나 다중상속은 꼭 필요한 경우가 아니면 사용을 남발하지 않도록 해야한다. 왜냐하면 동일한 메소드를 각 클래스에 갖고 있는 경우 출력시 원치 않는 결과값을 갖게 되기도 하는 등 프로그램을 개발하는데 읽기 쉬운 코드가 되지 않을 수 있다.
실습
➩삼각형 넓이를 계산하는 클래스를 만들고 이를 상속하는 클래스에서 getArea()를 오버라이딩 해서 출력 결과에 제곱센치미터가 추가 될 수 있도록 만들어보자
class Rectangular_calculator: def __init__(self,num1,num2): self.width = num1 self.height = num2 def getArea(self): result = self.width * self.height / 2 return result class Overriding(Rectangular_calculator): def __init__(self,num1,num2): super().__init__(num1,num2) def getArea(self): return str(super().getArea()) + 'cm²' a = Overriding(3,2) result = a.getArea() print(result)
하위 클래스의 getArea()메서드에서 상위 클래스를 강제 상속받아 문자열(str)로 고쳐준 다음 제곱센치미터를 붙여준 것을 확인 할 수 있다.(숫자+문자는 오류가 나므로 숫자를 문자형태로 바꾸어줍니다)
아래 그림과 같이 상위 클래스에서 하위 클래스에 메서드 구현을 강요하는 것이다. 보통 하위클래스에서 입맛에 맞게 구현을 할 수 있도록 상위클래스에서 적용하는 것으로 사용이 된다.

추상클래스를 사용하려면 다음과 같은 모듈을 import 해야한다.
from abc import abstractmethod class 함수명(metaclass=ABCMeta): @abstractmethod def 함수명
실습
➩계산기 추상 클래스를 만들고 이를 이용해서 새로운 계산기 클래스를 만들어보자. 추상 클래스에는 덧셈, 뺄셈, 곱셈, 나눗셈 기능이 선언되어 있어야 한다.
from abc import abstractmethod class Calculator(metaclass=ABCMeta): @abstractmethod def add(self,n1,n2): pass @abstractmethod def sub(self,n1,n2): pass @abstractmethod def mul(self,n1,n2): pass @abstractmethod def div(self,n1,n2): pass class UpgradeCalculator(Calculator): def add(self, n1, n2): print(n1+n2) def sub(self, n1, n2): print(n1-n2) def mul(self, n1, n2): print(n1*n2) def div(self, n1, n2): print(n1/n2) cal = UpgradeCalculator() cal.add(1,2) cal.sub(1,2) cal.mul(1,2) cal.div(1,2) #실행결과 3 -1 2 0.5
엄격하게 구분한다면 예외와 에러는 의미가 다르다. 에러는 문법적인 오류 또는 소프트웨어적인 오류를 얘기한다. 예외는 문법적인 문제는 없으나 실행 중 발생하는 예상하지 못한 문제이다.
예외를 처리하는 방법은 예외 발생 예상 구문을 try~except 로 감싸는 것이다.
실습
➩ 사용자로부터 숫자 5개를 입력받을 때 숫자가 아닌 자료형이 입력되면 예외 처리하는 프로그램을 만들어보자.
list = [] a = 1 while a < 6: try: number = int(input('숫자를 입력하세요:')) except: print('다시 입력해주세요.') continue list.append(number) a += 1 print('숫자:{}'.format(list)) #실행결과 숫자를 입력하세요:1 숫자를 입력하세요:4 숫자를 입력하세요:1 숫자를 입력하세요:3 숫자를 입력하세요:2 숫자:[1, 4, 1, 3, 2] #숫자가 아닌 문자를 입력했을 경우 실행결과 숫자를 입력하세요:1 숫자를 입력하세요:coffee 다시 입력해주세요. 숫자를 입력하세요:
~else 는 예외가 발생하지 않은 경우 실행하는 구문이다.
위에서 배운 try ~except 이후 넣어준다.
실습
eveList = [] #짝수 리스트 oddList = [] #홀수 리스트 floatList = [] #실수 리스트 n = 1 while n < 6: try: num = float(input('숫자를 입력 하세요:')) except: print("예외 발생") continue else: if num % 2 == 0: eveList.append(int(num)) elif num % 2 == 1: oddList.append(int(num)) elif num % 2 != 1: floatList.append(num) n += 1 print('짝수리스트:{}'.format(eveList)) print('홀수리스트:{}'.format(oddList)) print('실수리스트:{}'.format(floatList)) #실행결과 짝수리스트:[2] 홀수리스트:[1, 5] 실수리스트:[2.1, 3.2]
finally 는 어떠한 작업을 하는 과정에서 예외 발생과 상관없이 항상 실행한다.
즉 위에서 본 try~except 에서 예외가 발생하든 else에서 예외가 발생하지 않든 상관없이 무조건 실행한다라는 의미이다.
실습(바로 위 예제와 동일문제)
eveList = [] #짝수 리스트 oddList = [] #홀수 리스트 floatList = [] #실수 리스트 sumList = [] n = 1 while n < 6: try: data = input('숫자를 입력 하세요:') floatNum = float(data) except: print("예외 발생") continue else: if floatNum % 2 == 0: eveList.append(int(floatNum)) elif floatNum % 2 == 1: oddList.append(int(floatNum)) elif floatNum % 2 != 1: floatList.append(floatNum) n += 1 finally: sumList.append(data) print('짝수리스트:{}'.format(eveList)) print('홀수리스트:{}'.format(oddList)) print('실수리스트:{}'.format(floatList)) print('데이터 집합:{}'.format(sumList)) #실행결과 숫자를 입력 하세요:파이썬 예외 발생 숫자를 입력 하세요:1 숫자를 입력 하세요:3 숫자를 입력 하세요:1.3 숫자를 입력 하세요:8 숫자를 입력 하세요:10 짝수리스트:[8, 10] 홀수리스트:[1, 3] 실수리스트:[1.3] 데이터 집합:['파이썬', '1', '3', '1.3', '8', '10']
문자열 '파이썬'을 입력해여 예외가 발생했지만 입력한 모든 데이터가 출력되게끔 finally가 묶여서 위와 같이 모두 출력되는 결과를 갖는다.
예외 담당 클래스 Exception
예외가 발생하면 어떤 이유로 예외가 발생했는지를 명시해줄 수 있는 기능이다.
위의 예제를 다시 불러와 except 구문에 다음과 같이 표시해보고 실행을 돌려보자
except Exception as e: print('예외 발생 원인:{}'.format(e)) continue #실행결과 숫자를 입력 하세요:파이썬 예외 발생 원인:could not convert string to float: '파이썬 '
위와 같이 float 실수형이 아닌 파이썬이라는 문자열을 입력했기 때문에 예외가 발생 했음을 알 수 있다.
강제로 예외를 발생시킬 수 있는 키워드 이다.
실습
def sendSMS(msg): if len(msg) > 10: raise Exception('길이초과. MMS전환 후 발송', 1) else: print('SMS 발송') def sendMMS(msg): if len(msg) <= 10: raise Exception('길이 미달. SMS 으로 발송', 2) else: print('MMS 발송') msg = input('문자 메시지 입력:') try: sendMMS(msg) except Exception as e: print('e: {}'.format(e.args[0])) print('e: {}'.format(e.args[1])) if e.args[1] == 1: sendMMS(msg) elif e.args[1] == 2: sendSMS(msg) #실행결과 문자 메시지 입력:안녕하세요 e: 길이 미달. SMS 으로 발송 e: 2 SMS 발송
*참고(위의 예제는 try구문에서 어떤 함수를 실행시키냐에따라 기준이 달라져서 결과가 달라지게된다.)
파이썬에서는 외부파일을 불러와 읽기('r'), 쓰기('w') 기능을 할 수 있다. 아래 코드는 컴퓨터상 제로베이스취업스쿨이라는 폴더안에 메모장을 open 하여 쓰기('w')를 하였다.
참고로 폴더안에 파일이 없는 상태여도 'open'을 하고 경로주소값을 입력하면 폴더안에 새로운 파일이 생성된다.
파일은 open() → 'w','r'→ close() 순으로 열고 닫아야 한다.(순서 중요!!)
file = open('/Users/seayoon/Desktop/제로베이스취업스쿨/text.txt','w') strCnt = file.write('hello world!') print(strCnt) #문자열의 길이를 출력하였다. file.close() #실행결과 12
import time lt = time.localtime() datestr = '['+'오늘날짜:'+str(lt.tm_year)+'년'+str(lt.tm_mon)+'월'\ +str(lt.tm_mday)+'일]' file = open('/Users/seayoon/Desktop/제로베이스취업스쿨/text.txt','w') str = input('오늘의 할일:') file.write(datestr+str) file.close()

1) 쓰기(w) 모드로 열어줌, 내용을 작성하여 저장한다.
file = open('/Users/seayoon/Desktop/제로베이스취업스쿨/test.txt','w') writing = file.write(' 클래스 내에 정의된 함수를 메서드라고 부른다고 했습니다. 그리고 메서드의 첫 번째 인자는 항상 self여야 한다고 했습니다. 하지만 메서드의 첫 번째 인자가 항상 self여야 한다는 것은 사실 틀린 말입니다. 이번 절에서는 파이썬 클래스에서 self의 정체를 확실히 이해해 봅시다.') file.close()
2) 읽기(r) 모드로 열어줌, 특정단어를 변경해준다.(replace함수 사용), 마찬가지로 변경 후 파일 닫아줌
file = open('/Users/seayoon/Desktop/제로베이스취업스쿨/test.txt','r') reading = file.read() print(reading) changedWord = reading.replace('self','셀프') print(changedWord) file.close()
3) 쓰기(w)모드로 열어주고 변경된 문장(changedWord)를 덮어 씌어준다. 파일을 닫아준다
file = open('/Users/seayoon/Desktop/제로베이스취업스쿨/test.txt','w') file.write(changedWord) file.close()
4) 결과물


오늘은 파이썬에서 너무 중요한 개념을 많이 배운것 같다.
객체지향프로그래밍, 클래스, 생성자, 메서드 등 용어부터 낯설었는데 제대로 안익혀두면 나중에 강의교수님이 하는 말도 무슨말인지 모를것같다는 생각이 들었다.
그리고 평소에 간식 잘 안먹는데 코딩할때 자꾸 뭔가 먹고싶은 이유는 뭘까...ㅎㅎ
