- 객체와 메모리
변수는 객체의 메모리 주소를 저장하고 이를 이용해서 객체를 참조한다.
변수에 객체의 메모리 주소가 저장된다.
이러한 변수를 레퍼런스 변수라고 한다.
class Robot:
def __init__(self, color, height, weight):
self.color = color
self.height = height
self.weight = weight
def printRobotInfo(self):
print(f'color : {self.color}')
print(f'height : {self.height}')
print(f'weight : {self.weight}')
rb1 = Robot('red', 200, 80) # rb1은 Robot객체의 메모리 주소 값을 가지고 있는 레퍼런스 변수 이다.
rb2 = Robot('blue', 300, 120)
rb3 = rb1 # rb3에 rb1이 가지고 있는 메모리 주소가 들어가는 것!
rb1.printRobotInfo()
rb2.printRobotInfo()
rb3.printRobotInfo()
rb1.color = 'gray'
rb1.height = 250
rb1.weight = 100
rb1.printRobotInfo()
rb2.printRobotInfo()
rb3.printRobotInfo()
- 실습
국어, 영어, 수학 점수를 입력받아 리스트에 저장하고 원본을 유지한 상태로, 복사본을 만들어 과목별 점수를 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)}')
print(f'이후 총점: {sum(copyScores)}')
print(f'이전 평균: {sum(scores) / len(scores)}')
print(f'이후 평균: {sum(copyScores) / len(copyScores)}')
eunmerate를 사용하 for문 검색을 통해 추가적으로 공부!
- 얕은 복사
얕은 복사란, 객체 주소를 복사하는 것으로 객체 자체가 복사되지 않는다.
- 깊은 복사
깊은 복사란, 객체 자체를 복사하는 것으로 또 하나의 객체가 만들어진다.
class TemCls:
def __init__(self, n, s):
self.number = n
self.str = s
def printClsInfo(self):
print(f'self.number: {self.number}')
print(f'self.str: {self.str}')
얕은 복사 코드
얕은 복사
tc1 = TemCls(10, 'Hello')
tc2 = tc1
tc1.printClsInfo()
tc2.printClsInfo() # 얕은 복사 이므로 주소만 참조되어 같은 값이 나온다.
tc2.number = 3.14
tc2.str = 'Bye'
tc1.printClsInfo() # 얕은 복사 이므로 위에 tc2만 수정 했는데도 tc1또한 바뀐다.
tc2.printClsInfo()
깊은 복사 코드
import copy
tc1 = TemCls(10, 'Hello')
tc2 = copy.copy(tc1) # 객체가 복사 되는 것이므로 tc1과 tc2가 가리키는 주소가 다르다.
tc1.printClsInfo()
tc2.printClsInfo()
tc2.number = 3.14
tc2.str = 'Bye'
tc1.printClsInfo() # 깊은 복사 이므로 tc1은 수정되지 않는다.
tc2.printClsInfo()
- 다양한 복사 방법
scores = [9, 8, 5, 7, 6, 10]
scoresCopy = []
scoresCopy = scores # 얕은 복사
print(f'id(scores): {id(scores)}')
print(f'id(scoresCopy): {id(scoresCopy)}') # 얕은 복사 이므로 둘다 같은 주소값 출력
깊은 복사 (for문 사용)
scores = [9, 8, 5, 7, 6, 10]
scoresCopy = []
for s in scores: # 깊은 복사가 된다
scoresCopy.append(s)
print(f'id(scores): {id(scores)}')
print(f'id(scoresCopy): {id(scoresCopy)}')
scoresCopy.extend(scores) # extend를 활용한 깊은 복사
print(f'id(scores): {id(scores)}')
print(f'id(scoresCopy): {id(scoresCopy)}')
scoresCopy = scores.copy() # copy를 활용한 깊은 복사
print(f'id(scores): {id(scores)}')
print(f'id(scoresCopy): {id(scoresCopy)}')
scoresCopy = scores[:] # 슬라이싱을 활용한 깊은 복사
print(f'id(scores): {id(scores)}')
print(f'id(scoresCopy): {id(scoresCopy)}')
- 실습
선수의 원본 점수를 이용해서 평균을 출력하고, 최고값과 최저값을 제외한 평균을 출력하는 프로그램을 만들어보자.
playerOriginScore = [8.7, 9.1, 8.9, 9.0, 7.9, 9.5, 8.8, 8.3]
playerCopyScore = playerOriginScore.copy() # 깊은 복사
playerOriginScore.sort()
playerCopyScore.sort()
playerCopyScore.pop(0)
playerCopyScore.pop()
print(f'playerOriginScore : {playerOriginScore}')
print(f'playerCopyScore : {playerCopyScore}')
originTotal = round(sum(playerOriginScore), 2)
originAvg = round(originTotal / len(playerOriginScore), 2)
print(f'Original Total : {originTotal}')
print(f'Original Average : {originAvg}')
copyTotal = round(sum(playerCopyScore), 2)
copyAvg = round(originTotal / len(playerCopyScore), 2)
print(f'Copy Total : {copyTotal}')
print(f'Copy Average : {copyAvg}')
print(f'OriginAvg - CopyAvg : {originAvg - copyAvg}')
- 클래스 상속
클래스는 또 다른 클래스를 상속해서 내 것처럼 사용할 수 있다.
class1
↑ Class2는 Class1을 상속한다.
class2 → Class2는 Class1의 모든 기능을 사용할 수 있다.
↑ Class3는 Class2을 상속한다.
class3 → Class3는 Class2, Class1의 모든 기능을 사용할 수 있다.
class NormalCar:
def drive(self):
print('[NormalCar] drive() called!!')
def back(self):
print('[NormalCar] back() called!!')
class TurboCar(NormalCar): # NormalCar를 상속받아서 drive와 back 기능을 사용할 수 있다!
def turbo(self):
print('[TurboCar] turbo() called!!')
myTurboCar = TurboCar()
myTurboCar.turbo()
myTurboCar.drive()
myTurboCar.back()
- 실습
덧셈, 뺄셈 기능이 있는 클래스를 만들고, 이를 상속하는 클래스를 만들어서 곱셈과 나누셈 기능을 추가해 보자.
class CalculatorSuper:
def add(self, n1, n2):
return n1 + n2
def sub(self, n1, n2):
return n1 - n2
class CalculatorChild(CalculatorSuper): # CalculatorSuper를 상속 받아서 덧샘, 뺄셈도 가능하다
def mul(self, n1, n2):
return n1 * n2
def div(self, n1, n2):
return n1 / n2
myCalculator = CalculatorChild()
print(myCalculator.add(10, 20))
print(myCalculator.sub(10, 20))
print(myCalculator.mul(10, 20))
print(myCalculator.div(10, 20))
- 생성자
객체가 생성될 때 생성자를 호출하면 init()가 자동 호출된다.
Calculator init() 호출
↑
Calculator() 생성자 호출
↑
cal = Calculator()
class Calculator:
def __init__(self):
print('[Calculator] __init__() called!!')
cal = Calculator()
- init()가 속성을 초기화 한다.
매개변수를 받아서 초기화
# __init__()가 속성을 초기화 한다.
class Calculator:
def __init__(self, n1, n2): # 매개변수를 받아서 초기화
print('[Calculator] __init__() called!!')
self.num1 = n1
self.num2 = n2
cal = Calculator(10, 20)
print(f'cal.num1 : {cal.num1}') # 초기화 확인
print(f'cal.num2 : {cal.num2}') # 초기화 확인
매개변수를 받지 않고 고정 값으로 초기화
class Calculator:
def __init__(self): # 매개변수를 받지 않고 10과 100으로 고정 값으로 초기화
print('[Calculator] __init__() called!!')
self.num1 = 10
self.num2 = 100
cal = Calculator()
print(f'cal.num1 : {cal.num1}') # 초기화 확인
print(f'cal.num2 : {cal.num2}') # 초기화 확인
- super()
상위 클래스의 속성을 초기화하기 위해서 super()를 이용한다.
class P_Class: # 부모 클래스
def __init__(self, pNum1, pNum2):
print('[P_Class] __init__() called!!')
self.pNum1 = pNum1
self.pNum2 = pNum2
class C_Class(P_Class): # 부모 클래스
def __init__(self, cNum1, cNum2):
print('[C_Class] __init__() called!!')
# P_Class.__init__(self, cNum1, cNum2) # 강제로 호출
super().__init__(cNum1, cNum2) # 상위 클래스 호출, self를 안써도 괜찮다
self.cNum1 = cNum1
self.cNum2 = cNum2
cls = C_Class(10, 20)
- 실습
중간고사 클래스와 기말고사 클래스를 상속관계로 만들고 각각의 점수를 초기화하자. 또한 총점 및 평균을 반환하는 기능도 만들어보자.
class MidExam:
def __int__(self, s1, s2, s3):
print('[MidExam] __init__()') # MidExam 클래스 호출을 알림
self.mid_kor_score = s1
self.mid_eng_score = s2
self.mid_mat_score = s3
def printScores(self):
print(f'mid_kor_score: {self.mid_kor_score}')
print(f'mid_eng_score: {self.mid_eng_score}')
print(f'mid_mat_score: {self.mid_mat_score}')
class FinalExam(MidExam):
def __init__(self, s1, s2, s3, s4, s5, s6): # s1~3 중간고사 점수
print('[FinalExam] __init__()') # FinalExam 클래스 호출을 알림
super().__init__(self, s1, s2, s3)
self.final_kor_score = s4
self.final_eng_score = s5
self.final_mat_score = s6
def printScores(self):
super().printScores()
print(f'final_kor_score: {self.final_kor_score}')
print(f'final_eng_score: {self.final_eng_score}')
print(f'final_mat_score: {self.final_mat_score}')
def getTotalScore(self): # 총점
total = self.mid_kor_score + self. mid_eng_score + self.mid_mat_score
total += self.final_kor_score + self. final_eng_score + self.final_mat_score
return total
def getAverageScore(self): # 평균
return self.getTotalScore() / 6
exam = FinalExam(85, 90, 88, 75, 85, 95)
exam.printScores()
print(f'Total: {exam.getTotalScore()}')
print(f'Average: {round(exam.getAverageScore())}')
- 다중 상속
2개 이상의 클래스를 상속한다.
car → car1
car → car2
car → car3
class Car01:
def drive(self):
print(f'drive() method Called!!')
class Car02:
def turbo(self):
print(f'turbo() method Called!!')
class Car03:
def fly(self):
print(f'fly() method Called!!')
class Car(Car01, Car02, Car03):
def __int__(self):
pass
myCar = Car()
myCar.drive()
myCar.turbo()
myCar.fly()
- 실습
BasicCalculator와 DeveloperCalculator 클래스를 다음과 같이 만들고
이들 클래스를 상속해서 Calculator 클래스를 만들고 사용해 보자.
class BasicCalculator:
def add(self, n1, n2):
return n1 + n2
def sub(self, n1, n2):
return n1 - n2
def mul(self, n1, n2):
return n1 * n2
def div(self, n1, n2):
return n1 / n2
class DeveloperCalculator:
def mod(self, n1, n2):
return n1 % n2
def flo(self, n1, n2):
return n1 // n2
def exp(self, n1, n2):
return n1 ** n2
class MyCalculator(BasicCalculator, DeveloperCalculator):
def __init__(self):
pass
cal = MyCalculator()
print(f'cal.add(10, 20) : {cal.add(10, 20)}')
print(f'cal.sub(10, 20) : {cal.sub(10, 20)}')
print(f'cal.mul(10, 20) : {cal.mul(10, 20)}')
print(f'cal.div(10, 20) : {cal.div(10, 20)}')
print(f'cal.mod(10, 20) : {cal.mod(10, 20)}')
print(f'cal.flo(10, 20) : {cal.flo(10, 20)}')
print(f'cal.exp(10, 5) : {cal.exp(2, 5)}')
다중상속을 남발해서는 안된다!
- 오버라이딩
하위 클래스에서 상위 클래스의 메서드를 재정의(override)한다.
Robot [ fire() -> '총알 발사' ]
↑ 상속 ↑ 메서드 오버라이딩
NewRobot [ fire() -> '레이저 발사' ]
class Robot():
def __init__(self, c, h, w):
self.color = c
self.height = h
self.weight = w
def fire(self):
print('미사일 발사!!')
def printRobotInfo(self):
print(f'self.color : {self.color}')
print(f'self.height : {self.height}')
print(f'self.weight : {self.weight}')
class NewRobot(Robot):
def __init__(self, c, h, w):
super().__init__(c, h, w)
def fire(self): # 오버라이딩
print('레이저 발사!!')
myRobt = NewRobot('red', 230, 300)
myRobt.printRobotInfo()
myRobt.fire()
- 실습
삼각형 넓이를 계산하는 클래스를 만들고 이를 상속하는 클래스에서
getArea()를 오버라이딩 해서 출력 결과가 다음과 같을 수 있도록 클래스를 만들어보자.
class TriangleArea:
def __init__(self, w, h):
self.width = w
self.height = h
def printTriangleAreaInfo(self):
print(f'self.width : {self.width}')
print(f'self.height : {self.height}')
def getArea(self):
return self.width * self.height / 2
class NewTriangleArea(TriangleArea):
def __init__(self, w, h):
super().__init__(w, h)
def getArea(self):
return str(super().getArea()) + 'cm' # 이 부분 다시 잘 볼 것!
ta = NewTriangleArea(7, 5)
ta.printTriangleAreaInfo()
triangleArea = ta.getArea()
print(f'triangleArea : {triangleArea}')
- 추상클래스
상위 클래스에서 하위클래스에 메서드 구현을 강요한다.
AirPlane [ flight() 선언만 ]
↑ 상속 ↓ 메서트 구체화
Airliner [ flight() 구현 ] # 여기서 구현을 안하면 에러가 난다!
- 상속을 받았을 때 각자 설정하여 사용할 때 사용!
- from abc import ABCMeta # 추상클래스를 위해 가져와야함
- from abc import abstractmethod # 추상클래스를 위해 가져와야함
from abc import ABCMeta # 추상클래스를 위해 가져와야함
from abc import abstractmethod # 추상클래스를 위해 가져와야함
class AirPlane(metaclass=ABCMeta):
@abstractmethod # 구현 강제
def flight(self): # 구현이 안 되어 있음
pass
def forward(self):
print('전진!!')
def backward(self):
print('후진!!')
class Airliner(AirPlane):
def __init__(self, c):
self.color = c
def flight(self):
print('시속 400km/h 비행!!')
class FighterPlane(AirPlane):
def __init__(self, c):
self.color = c
def flight(self):
print('시속 700km/h 비행!!')
al = Airliner('red')
al.flight()
al.forward()
al.backward()
fl = FighterPlane('blue')
fl.flight()
fl.forward()
fl.backward()
- 실습
계산기 추상 클래스를 만들고 이를 이용해서 새로운 계산기 클래스를 만들어 보자.
추상 클래스에는 덧셈, 뺄셈, 곱셈, 나눗셈 기능이 선언되어 있어야 한다.
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 DeveloperCalculator():
def add(self, n1, n2):
return n1 + n2
def sub(self, n1, n2):
return n1 - n2
def mul(self, n1, n2):
return n1 * n2
def div(self, n1, n2):
return n1 / n2
cal = DeveloperCalculator()
print(f'cal.add(10, 20) : {cal.add(10, 20)}')
print(f'cal.sub(10, 20) : {cal.sub(10, 20)}')
print(f'cal.mul(10, 20) : {cal.mul(10, 20)}')
print(f'cal.div(10, 20) : {cal.div(10, 20)}')
- 예외란?
예외란, 문법적인 문제는 없으나 실행 중 발생하는 예상하지 못한 문제이다.
- 예외 종류
- 예외 관련 클래스는 Exception 클래스를 상속한다.
Exception ← ArithmeticError ← ZeroDivisionError
Exception ← Environment Error ← IOError
Exception ← LookupError ← IndexError
Exception ← SyntaxError ← IndentationError
def add(n1, n2):
print(n1 + n2)
def div(n1, n2):
print(n1 / n2)
fn = int(input('input firstNum : '))
sn = int(input('input secondNum : '))
add(fn, sn)
div(fn, sn) # (10과 0을 넣으면 ZeroDivisionError가 발생한다)
print(10/0) # ZeroDivisionError가 발생한다
print(int('hello')) # ValueError: invalid literal for int() with base 10: 'hello'
lists = [1, 2, 3, 4, 5, 6]
print(lists[0]) # 정상 실행
print(lists[1]) # 정상 실행
print(lists[2]) # 정상 실행
print(lists[3]) # 정상 실행
print(lists[4]) # 정상 실행
print(lists[5]) # 정상 실행
print(lists[6]) # IndexError: list index out of range
이 글은 제로베이스 데이터 취업 스쿨의 강의자료 일부를 발췌하여 작성되었습니다.