데이터 취업 스쿨 스터디 노트 -(7) 객체지향, 클래스, 복사, 상속, 오버라이딩, 예외처리

테리·2024년 6월 10일
0

객체지향 프로그래밍

객체를 이용한 프로그램으로 객체는 속성과 기능으로 구성된다.
객체의 의미를 프로그래밍으로 가져와서 프로그램으로 만듬.

계산기

속성: 숫자 등등...
기능: 덧셈, 뺄셈 등등...

자동차

속성: 색상, 길이, 가격 등등...
기능: 전진, 후진, 정지 등등...

객체 만들기(생성)


클래스는 객체를 만들기 위한 하나의 틀이다.
클래스는 한개이지만 객체는 내가 원하는대로 계속해서 생성할 수 있다.
우리는 프로그램 언어로 클래스만 만들면 된다.

객체 ★★★★★
객체란 데이터(실체)와 그 데이터에 관련되는 동작을 모두 포함한 개념.
ex) 기차역에서 승차권을 발매하는 경우 실체인 '손님'과 동작인 '승차권 주문'하나의 객체이다

객체 사용의 장점

코드 재사용, 모듈화에 좋다.

클래스 만들기

  • 클래스명은 대문자로 시작한다.
  • 클래스 내의 함수를 '메소드'라고 한다.
  • 생성자 __init__은 객체를 초기화 한다.
  • 소괄호 안에 있는 것들이 매개변수
  • self를 사용하여 클래스 내 멤버에 접근할 수 있다. self.x라고 하면 인스턴스 변수 x에 접근할 수 있는 것이고 self.c1()라고 쓰면 인스턴스 메소드 즉 c1이라는 함수에 접근할 수 있는 것이다.

class Car:

    def __init__(self, col, len):
        self.color = col #self는 나 자신(=Car)를 의미함. 속성을 정함.
        self.length = len

    def doStop(self): #클래스 안에 포함된 기능은 매개변수를 self로 받아야함.
        print('Stop!')

    def doStart(self):
        print('Start!')

#class는 객체를 만들기 위한거기 때문에 실행해도 아무 기능도 실행 안됨.


생성자는 클래스 이름과 동일함. Car()

실행 방법

car1 = Car('red', 200) #속성을 넣어줌
car1.doStop() #클래스 안의 함수를 불러옴

객체 속성 변경

위의 Class를 예시로 속성을 변경하자면

car1.color = 'White'
car1.length = 400

이렇게 선언해주면 클래스가 바뀜

실습 아주 중요한 인사이트

위 코드에서 아주 중요한 부분은 바로
cal = Caculator() 부분이다.
해당 부분 없이 Caculator.add()를 사용하면 에러가 발생한다.
왜 그런것인가?

#cal을 Caculator()라는 클래스의 인스턴스라고 한다.
#클래스는 속성과 기능(행동)을 정의하는 설계도와 같다.
#그렇기에 인스턴스는 해당 클래스의 특징을 가지며 독립적인 상태를 유지할 수 있다.
#따라서 Caculator()라는 클래스의 인스턴스를 생성하는 작업을 하지 않으면 해당 클래스를 사용할 수 없다.

그렇기에 cal = Caculator()라는 인스턴스를 생성한 뒤
cal.add()를 사용할 수 있다.

객체와 메모리


우리는 변수를 사용하면 변수안에 저장된 객체의 메모리 주소를 불러와 사용하는 것임. 이런 변수를 레퍼런스 변수라고 한다.

class Robot:
	def 생략
    
    def 생략
    
#생성된 객체가 있는 메모리의 주소값을 rb1에 넣어줌
rb1 = Robot('red', 200, 300)
rb2 = Robot('blue', 100, 400)

만약
rb3 = rb1 이렇게 선언한다면 객체가 복사된 것이 아니라 rb1에 있는 객체의 메모리 주소값이 rb3으로 들어간 것이다.
이를 얕은 복사라고 한다.
결과적으로 rb3와 rb1은 하나의 객체를 공유하고 있는 것이다.
rb1을 사용하나 rb3을 사용하나 똑같다.

만약
rb1.color = 'gray'라고 속성값을 변경해도
rb1만 변경되는 것이 아니라 rb3도 변경되는 것이다.
why? 둘다 같은 객체의 메모리 주소를 바라보고 있으므로.

실습 팁
enumerate 함수는 index와 함께 튜플 형태로 리턴함
ex)
for 인덱스 변수, 변수 enumerate(['A', 'B', 'C'])
print(인덱스 변수, 변수)

얕은 복사

얕은 복사란, 객체 주소를 복사하는 것으로 객체 자체가 복사되지 않는다.

tc1과 tc2의 값은 항상 같다.

깊은 복사

깊은 복사란, 객체 자체를 복사하는 것으로 또 하나의 객체가 만들어진다.

tc1의 값은 변하지 않고 tc2의 값만 변한다

scores = [9,8,7,6,5]
scoresCopy =[]

#얕은 복사
scoresCopy = scores
print(id(scores))
print(id(scoresCopy))
scores = [9,8,7,6,5]
scoresCopy =[]

#깊은 복사
for s in scores:
    scoresCopy.append(s)
print(id(scores))
print(id(scoresCopy))
#깊은복사
scoresCopy.extend(scores)
print(id(scores))
print(id(scoresCopy))
#깊은복사
scoresCopy = scores.copy()
#깊은복사
scoresCopy = scores[:]

클래스 상속


상속하는 방법

class A:
	def drive(self):
	생략

class B(A):
	def 생략

B클래스를 사용하면 A클래스의 기능을 모두 사용할 수 있다.
B.drive()

생성자


cal 이라는 레퍼런스 변수를 통해 Calculator()클래스를 정의하면 Calculator()생성자가 생성됨. 그러면 init이 자동 호출됨


C_Class를 사용할 때 __init__가 자동호출이 되는데 이때 때 P_Class의 속성값을 초기화 해주면 된다. 그래서 __init__가 있는 부분에서 초기화를 해주면 됨.

방법1

P_Class.__init__(self, cNum1, cNum2)
를 사용해 속성값을 초기화 함.

방법2

super().__init__(cNum1, cNum2)
상위(부모 클래스)로 올라가서 속성값을 초기화 함.

다중상속

괄호안에 상속받고 싶은 클래스를 모두 사용하면 됨.
ex)

class A(B, C):
	def __init__(self):
    pass

오버라이딩

메서드를 재정의한다.

이렇게 한다고 Robot.fire()의 값까지 바뀌는 것은 아니다.

추상클래스

상위 클래스에서 하위 클래스에 메서드 구현을 강요한다.
상위 클래스에 def로 정의된 함수(메서드)에 내용이 없기 때문에 하위 클래스에서 구현해야함.

어떤 특정기능을 상속 받았을 때 각자 입맞에 맞게 알아서 사용할 때 사용.
아래의 부분이 꼭 필요함.

from abc import ABCMeta
from abc import abstractmethod

class AirPlane(metaclass=ABCMeta):

    @abstractmethod
    def flight(self):
        pass

예외란?

예외란, 문법적인 문제는 없으나 실행 중 발생하는 예상하지 못한 문제
ex) 0으로 나눌때, int('문자')

예외처리

예상하지 못한 예외가 프로그램 전체 실행에 영향이 없도록 처리함.
발생한 예외에 대해서 별도 처리함.

try ~ except ~ else

~else 예외가 발생하지 않는 경우 실행하는 구문이다.
꼭 try, except를 사용했을 때만 else를 사용할 수 있다.

finally

예외가 발생하던 안하던 상관없이 항상 실행한다.

Exception 클래스

예외가 발생하면 예외가 발생하는 클래스를 불러와서 어떤 예외가 발생했는지 확인하는 것.

Exception이 너무 길어서 as e로 대신 사용

num1 = 10
num2 = 0
try:
    print(num1 / num2)
except Exception as e:
    print('예외발생')
    print(f'exception: {e}')

raise 키워드를 이용하면 예외를 강제로 발생시킬 수 있다.

  • 이때 우리가 원하는 문구가 나타나도록 할 수 있다.
ex) raise Exception('원하는 문구')
  • try를 사용해서 raise로 예외를 발생시키면 except를 사용해 해당 예외처리 클래스를 출력해줘야 코드상 에러가 발생하지 않음.
    ex)
try:
    if len(adminPw) < 5:
        raise 예외처리 클래스(adminPw)
except 예외처리 클래스 as e1:
    print(e1)
  • try를 사용하지 않고 if문 안에 그냥 바로 raise 예외처리 클래스를 사용할 수 있음. 그런데 보기 좋게 출력되지는 않음.(가공 처리가 안되어 있음)
def divCalculator(n1,n2):
    if n2 != 0:
        print(n1 / n2)
    else:
        raise Exception('0으로 나눌 수 없습니다.')

num1 = int(input('number1: '))
num2 = int(input('number2: '))

try:
    divCalculator(num1,num2)
except Exception as e:
    print(e)
    
#결과
#number1: 10
 number2: 0
 0으로 나눌 수 없습니다.

사용자 예외 '클래스'

내가 예외 '클래스'를 직접 만듬

super().__init__(예외 발생 문구)에서 예외 발생 문구를 적어줘야 한다. 기본적으로 제공하는 Exception 클래스를 부모 클래스로 가져온다고 한들 애초에 없는 에러를 내가 만든거기 때문에 괄호 부분을 비워두면 아무런 문구도 뜨지 않는다.

class PasswordLengthShortException(Exception):
    def __init__(self,str):
        super().__init__(f'{str}: 길이 10초과!')

0개의 댓글