메모리와 객체에 대한 이해
객체는 class로부터 생성자를 호출해서 생성되고, 메모리에 생성된다.
= 어떤 클래스가 하나 있을 때 생성자를 호출하면, 메모리에 객체가 생성된다.
생성한 객체를 사용하기 위해서는, 변수를 만들어서 객체를 할당해 사용된다.
= *객체는 변수에 객체데이터로 들어가는게 아니고, 변수는 다른 곳에 저장돼있다.
예) Car라는 클래스로부터 객체를 만들어서, myCar라는 변수에 저장했다?
-> 생성된 객체는 다른 곳에 저장돼있고,
-> myCar라는 변수는 또 다른 곳에 저장돼있는 것
= 변수에 직접 객체가 저장 안 되는데,
객체가 생성됐을 때 메모리 주소가 변수에 저장되는 것
= 실제로 변수안에 있는 객체가 아니라, 객체의 메모리 주소를 이용하는 것
= 변수에 어떤 명령을 내리면 그 주소를 찾아가서 그 객체의 기능이나 속성이 호출되거나 변경되는 것.
레퍼런스 변수 = 주소값을 가지고 객체를 참조하는 변수
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)
#class를 이용해서 객체를 생성할 수 있다.
#생성자 호출하기 = class와 이름이 똑같다
#()안에 초기 속성값을 넣어줌
#rb1 같은 변수를 설정해줘야 접근이 가능함
#rb1이라는 레퍼런스변수에 바로 들어가는게 아니라, 생성된 객체가 있는 메모리의 주소값을 넣어줌
rb1.printRobotInfo()
#명령을 내리면, 주소값을 알고 있으니까 주소에 가서 printRobotInfo라는 매소드를 찾아서 호출하는 기능이 실행되는 것
rb2 = Robot('Blue', 300, 120)
rb3 = rb1 #얕은 복사 #rb1의 메모리 주소값이 rb3에 다시 할당되는 것
rb1.printRobotInfo()
rb2.printRobotInfo()
rb3.printRobotInfo() #그 결과로 값이 똑같음
rb1.color = 'gray'
rb1.height = '250'
rb1.weight = '100'
rb1.printRobotInfo()
rb2.printRobotInfo()
rb3.printRobotInfo() #rb1의 수정결과가 rb3의 값과 똑같음. 하나의 레퍼런스변수를 수정하면 다른쪽에서 가르키는 값도 바뀐다.
-->
color : red
height : 200
weight : 80
color : Blue
height : 300
weight : 120
color : red
height : 200
weight : 80
color : gray
height : 250
weight : 100
color : Blue
height : 300
weight : 120
color : gray
height : 250
weight : 100
scores = [int(input('국어점수 입력:')),
int(input('영어점수 입력: ')),
int(input('영어점수 입력: '))]
print(scores)
copyScores = scores.copy()
#복사하는 함수 = 주소만 복사한 게 아니라, 실제 똑같은 객체가 만들어진 것
for idx, scores in enumerate(copyScores):
result = scores * 1.1
copyScores[idx] = 100 if result > 100 else result
print(f'이전 평균: {sum(scores) / len(scores)}')
print(f'이후 평균: {sum(copyScores) / len(copyScores)}')
-->
File "C:\Users\User\Desktop\python_ex\5_020\work.py", line 14, in <module>
print(f'이전 평균: {sum(scores) / len(scores)}')
^^^^^^^^^^^
TypeError: 'int' object is not iterable
객체 복사에 대한 이해
하나의 객체를 공유해서 쓰는 두개의 변수
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()
객체가 하나 더 생기고, 새로운 변수가 만들어진다.
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()
'''
#깊은 복사
import copy
tc1 = TemCls(10, 'hello')
tc2 = copy.copy(tc1) #copy모듈의 copy라는 기능 + 레퍼런스 값을 넣어주면 객체를 새로 복사해서 할당해줌
tc1.printClsInfo()
tc2.printClsInfo()
tc2.number = 3.14
tc2.str = 'bye'
tc1.printClsInfo()
tc2.printClsInfo()
plaOriSco = [8.7, 9.1, 8.9, 9.0, 7.9, 9.5, 8.8, 8.3]
plaCopSco = plaOriSco.copy()
plaOriSco.sort()
plaCopSco.sort()
plaCopSco.pop(0)
plaCopSco.pop()
print(f'plaOriSco: {plaOriSco}')
print(f'plaCopSco: {plaCopSco}')
oriTot = round(sum(plaOriSco), 2)
oriAvg = round(oriTot / len(plaOriSco), 2)
print(f'original Total: {oriTot}')
print(f'original Average: {oriAvg}')
copTot = round(sum(plaCopSco), 2)
copAvg = round(oriTot / len(plaCopSco), 2)
print(f'copy Total: {copTot}')
print(f'copy Average: {copAvg}')
print(f'oriAvg - copAvg: {oriAvg - copAvg}')
-->
plaOriSco: [7.9, 8.3, 8.7, 8.8, 8.9, 9.0, 9.1, 9.5]
plaCopSco: [8.3, 8.7, 8.8, 8.9, 9.0, 9.1]
original Total: 70.2
original Average: 8.78
copy Total: 52.8
copy Average: 11.7
oriAvg - copAvg: -2.92
클래스의 기능을 내 것처럼 사용하자
class NormalCar:
def drive(self): #기능을 하나 만들어줌
print('[NormalCar] drive() called')
def back(self):
print('[NormalCar] back() called')
class TurboCar(NormalCar): #NormalCar 상속하면 기능 두개 가질수 있음
#class 다음에 () 안에 상속을 할 클래스를 명시해주면 끝
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):
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))
-->
30
-10
200
0.5
객체 생성에 대해 좀 더 자세히 알아보자
class Calculator:
def __init__(self):
print('[Calculator] __init__() called')
cal = Calculator()
# (눈에 보이지 않지만) 생성자를 호출하면
# __init__() 를 자동으로 호출해서
# 객체가 메모리에 만들어진다 -> [Calculator] __init__() called
-->
[Calculator] __init__() called
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, n):
print('[Calculator] __init__() called')
self.num1 = n
self.num2 = 100
cal = Calculator(3.14)
print(f'cal.num1 : {cal.num1}')
print(f'cal.num2 : {cal.num2}')
#하나는 매개변수로 값 넣고, 하나는 지정해둘 수도 있음
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')
# 쓰기 위한 방법 1
P_class.__init__(self, cNum1, cNum2)
# 쓰기 위한 방법 2
super().__init__(cNum1, cNum2)
# 상위로 올라가서, init을 호출하고, 값은 c1과 c2로 초기화해라
# self값 따로 안 써줘도 됨
self.cNum1 = cNum1
self.cNum2 = cNum2
cls = C_class(10, 20)
# C_class의 init method만 호출됨. = 속성도 초기화 돼서 값을 쓸 수 있는 것
# P_class의 속성값은 초기화 되지 않아서 쓸 수 없음
-
# 기능은 상속만 하면 바로 쓸 수 있는데
# 속성은 init이 호출돼야 초기화된다.
# super()로 강제적으로 init을 호출해서 속성을 초기화해서 사용할 수 있다.
-->
[C_class] __init__() called
[P_class] __init__() called
class MidExam:
def __init__(self, s1, s2, s3):
print('[MidExam] __init__()')
self.mid_kor_score = s1
self.mid_eng_score = s2
self.mid_mat_score = s3
def printScore(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 EndExam(MidExam):
def __init__(self, s1, s2, s3, s4, s5, s6):
#객체하나로 속성값도 한번에 초기화하기 위해
print('[EndExam] __init__()')
super().__init__(s1, s2, s3) #상위로 값 올려보내기위해
self.end_kor_score = s4
self.end_eng_score = s5
self.end_mat_score = s6
def printScore(self):
super().printScore()
print(f'end_kor_score: {self.end_kor_score}')
print(f'end_eng_score: {self.end_eng_score}')
print(f'end_mat_score: {self.end_mat_score}')
def getTotalScore(self):
total = self.mid_kor_score + self.mid_eng_score + self.mid_mat_score
total += self.end_kor_score + self.end_eng_score + self.end_mat_score
return total
def getAverageScore(self):
return self.getTotalScore() / 6
exam = EndExam(85, 90 ,88, 75, 85, 95)
exam.printScore()
print(f'TotalScore: {exam.getTotalScore()}')
print(f'AverageScore: {round(exam.getAverageScore(), 2)}')
-->
[EndExam] __init__()
[MidExam] __init__()
mid_kor_score: 85
mid_eng_score: 90
mid_mat_score: 88
end_kor_score: 75
end_eng_score: 85
end_mat_score: 95
TotalScore(): 518
AverageScore(): 86.33
2개 이상의 클래스를 상속한다
**필요할 때만 사용하기
( )안에 상속받고 싶은 클래스 이름을 나열하면 사용가능
class Car01:
def drive(self):
print('drive() method called')
class Car02:
def turbo(self):
print('turbo() method called')
class Car03:
def fly(self):
print('fly() method called')
class Car(Car01, Car02, Car03):
def __init__(self):
pass
myCar = Car()
myCar.drive()
myCar.turbo()
myCar.fly()
-->
drive() method called
turbo() method called
fly() method called
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 Calculator(BasicCalculator, DeveloperCalculator):
def __init__(self):
pass
cal = Calculator()
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(2, 5): {cal.exp(2, 5)}')
method를 재정의한다
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('레이저 발사')
myRobot = NewRobot('red', 200, 300)
myRobot.fire()
myRobot.printRobotInfo()
-->
레이저 발사
self.color : red
self.height : 200
self.weight : 300
class TriangleArea:
def __init__(self, w, h):
self.width = w
self.height = h
def printTriangleArea(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()) + '㎤'#str = 문자열변환
ta = NewTriangleArea(7, 5)
ta.printTriangleArea()
triangleArea = ta.getArea()
print(f'triangleArea: {triangleArea}')
-->
self.width: 7
self.height: 5
triangleArea: 17.5㎤
메서드 구현을 강요한다.
강제적으로 구현해서 써야한다는 클래스 = 추상클래스
from abc import ABCMeta #1. abc 패키지로부터 ~ 모듈을 가져와야함
from abc import abstractmethod #2. abc 패키지로부터 ~ 모듈을 가져와야함
class Airplane(metaclass=ABCMeta): #3. 강제성을 띄게 하는 상위클래스에 ~ 이렇게 명시해주고
@abstractmethod #4. 강제로 구현해야하는 메서드에 데코레이션을 붙여줌
def flight(self): #상속하는 child class에서 구현해줘야 오류안남
pass #5. -> 이 클래스를 상속하는 클래스에서 반드시 구현해줘야함.
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()
fp = FighterPlane('red')
fp.flight()
fp.forward()
fp.backward()
-->
시속 400km/h 비행
전진
후진
시속 700km/h 비행
전진
후진
from abc import ABCMeta
from abc import abstractmethod
class Calculator:
@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(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)
def mod(self, n1, n2):
print(n1 % n2)
def flo(self, n1, n2):
print(n1 // n2)
dc = DeveloperCalculator()
dc.add(10, 20)
dc.sub(10, 20)
dc.mul(10, 20)
dc.div(10, 20)
dc.mod(10, 20)
dc.flo(10, 20)
예상하지 못한 문제로 프로그램 실행이 어려운 상태
예)
def add(n1, n2):
print(n1 + n2)
def sub(n1, n2):
print(n1 - n2)
def mul(n1, n2):
print(n1 * n2)
def div(n1, n2):
print(n1 / n2)
firstNum = int(input('firstNum: '))
secondNum = int(input('secondNum: '))
add(firstNum, secondNum)
sub(firstNum, secondNum)
mul(firstNum, secondNum)
div(firstNum, secondNum)
-->
firstNum: 10
** secondNum: 0 # 예외 발생
10
10
0
** ZeroDivisionError: division by zero # 예외 발생
# 10 / 0 이 안 되기 때문에
엄격하게 에러와 예외를 구분할 수 있으나, 같은 의미로도 사용한다.
파이썬 뿐 아니라 다른 언어에서도 예외가 발생할 수 있고, 내부적으로 정의된 담당 클래스가 다 있다.
ZeroDivisionError
-> ArithmeticError : 수학관련
IOError : 입출력 관련
-> EnvironmentError
IndexError : 예) 길이가 5인데 index가 4일때
-> LookupError
IndetationError : 들여쓰기 에러
-> SyntaxError
...
=> Exception = 최상위 클래스
이것을 상속한 여러가지 예외를 담당하는 에러가 있다.
예)
print(10 / 0)
print(int('hello'))
lists = [1, 2, 3, 4, 5, 6]
print(lists[3])