Day12. 파이썬 중급 (5~6)

Junghwan Park·2023년 4월 19일
0

스터디노트

목록 보기
13/54

객체와 메모리

  • 객체와 메모리
    변수는 객체의 메모리 주소를 저장하고 이를 이용해서 객체를 참조한다.
    변수에 객체의 메모리 주소가 저장된다.
    이러한 변수를 레퍼런스 변수라고 한다.
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))

생성자 (01)

  • 생성자
    객체가 생성될 때 생성자를 호출하면 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)

생성자 (02)

  • 실습
    중간고사 클래스와 기말고사 클래스를 상속관계로 만들고 각각의 점수를 초기화하자. 또한 총점 및 평균을 반환하는 기능도 만들어보자.
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

이 글은 제로베이스 데이터 취업 스쿨의 강의자료 일부를 발췌하여 작성되었습니다.

profile
안녕하세요 반갑습니다^^

0개의 댓글