본문 바로가기
SSAFY

[수업기록] OOP : 객제지향프로그램(2), 에러와 예외

by 주니코니 2024. 7. 25.

240725 파이썬반

 

1. 상속

1) 개념

- 기존 클래스의 속성과 메서드를 물려받아 새로운 하위 클래스를 생성하는 것

- 코드 재사용(중복 코드를 줄일 수 있음)

- 계층 구조(하나의 큰 클래스가 아닌 단계적으로 계층구조를 만들어 더 구체적인, 관계가 있는 클래스 형성 가능)

- 유지 보수 용이(해당 클래스만 수정하면 됨 즉 수정이 필요한 범위 최소화 가능)

- 문법

class Person:
def 	


class Professor(Person): #하위클래스 문법
def

 

2) 다중 상속

- 둘 이상의 상위 클래스로부터 여러 행동이나 특징을 그대로 상속받게 되는 것

- 중복되는 속성이나 메서드가 있는 경우, 상속되는 순서에 의해 결정

class Person:
def


class Mom(Person):
def

class Dad(Person):
def

class FirstChild(Dad,Mom):
def

- 다이아몬드 문제(누굴 상속받아야하나? 모호함) :  MRO(메서드 결정 순서 함수)를 따른다

=>  super() : 자동으로 부모 클래스 객체를 반환하는 내장 함수

=> super() : 다중 부모 클래스의 경우, __init__ 없는 클래스의 경우 __int__이 있는 부모 클래스를 자동적으로 찾기 위함(코드가 복잡해질 때 개발자에게 편리함을..)

 

- super() : 단일 상속 구조에서도 쓰임(만일 부모 클래스 이름이 바뀐다면? ㅇㅊㅊㅊㅊ,, 최대한 수정이 적게 super()를 쓰면 되..)

# super 사용 전
class Person:
    def __init__(self, name, age, number, email):
        self.name = name
        self.age = age
        self.number = number
        self.email = email


class Student(Person):
    def __init__(self, name, age, number, email, student_id):
        self.name = name
        self.age = age
        self.number = number
        self.email = email
        self.student_id = student_id



# super 사용 예시 - 단일 상속
class Person:
    def __init__(self, name, age, number, email):
        self.name = name
        self.age = age
        self.number = number
        self.email = email

class Student(Person):
    def __init__(self,name,age,number,email,student_id,gpa):
        super().__init__(name, age, number, email) 
        #self 안넣어도됨 class Student(Person)에서 받고있어서
        #self.name = name 등 대체하는 간단 코드!
        self.student_id = student_id
        self.gpa = gpa 
    pass



# super 사용 예시 - 다중 상속
class ParentA:
    def __init__(self):
        self.value_a = 'ParentA'

    def show_value(self):
        print(f'Value from ParentA: {self.value_a}')


class ParentB:
    def __init__(self):
        self.value_b = 'ParentB'

    def show_value(self):
        print(f'Value from ParentB: {self.value_b}')


class Child(ParentA, ParentB):
    def __init__(self):
        # 총 인스턴스 두개 생김
        super().__init__() #self.value_a # ParentA
        self.value_c = 'Child'

child1 = Child()
print(child1.value_a)
print(child1.value_c)
print(child1.value_b) #AttributeError: 'Child' object has no attribute 'value_b'

 

 

3) 클래스 메서드 매개변수에 대해 

# 자식 클래스에서 부모 클래스의 클래스 메서드 호출하기


class Animal:
    total_count = 0

    def __init__(self, name):
        self.name = name
        Animal.total_count += 1

    @classmethod #객체가 없어도 쓸 수 있음
    def get_total_count(cls): 
        #cls : 클래스 자기자신을 의미하는 걸까? #self는 인스턴스 자기자신
        #cls : 클래스 메서드 호출하는 클래스(Animal)를 의미함
        #상속이란 개념 때문에 cls라 표기
        return f'전체 동물 수: {cls.total_count}'


class Dog(Animal):
    dog_count = 0

    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed
        Dog.dog_count += 1

    @classmethod
    def get_dog_info(cls):
        # cls.get_total_count()는 부모 클래스의 클래스 메서드를 호출하여 전체 동물 수를 출력
        return f'{cls.get_total_count()}, 강아지 수: {cls.dog_count}'
        #get_total_count()란 : Animal 클래스로 돌아가서 확인 

class Cat(Animal):
    cat_count = 0

    def __init__(self, name, breed):
        super().__init__(name) #Animal 클래스 매개변수 그대로 가져와야함
        self.breed = breed
        Cat.cat_count += 1

    @classmethod
    def get_cat_info(cls):
        # cls.get_total_count()는 부모 클래스의 클래스 메서드를 호출하여 전체 동물 수를 출력
        return f'{cls.get_total_count()}, 고양이 수: {cls.cat_count}'


dog1 = Dog('멍멍이', '삽살개')
dog2 = Dog('바둑이', '진돗개')
print(Dog.get_dog_info())  # 출력: 전체 동물 수: 2, 강아지 수: 2


cat1 = Cat('노아', '페르시안')
cat2 = Cat('루비', '코숏')
print(Cat.get_cat_info())  # 출력: 전체 동물 수: 4, 고양이 수: 2

 

4) mro 메서드 :

print(D.mro())

- mro 필요한 이유는 부모 클래스들이 여러번 엑세스 되지 않게, 각 부모 오직 한번 호출

 


2. 에러와 예외

1) 디버깅

- 오작동 원인을 식별하여 수정하는 작업

 

2) 파이썬 에러 유형

(1) 문법 에러 syntax error 프로그램 구문이 올바르지 않은 경우(오탈자, 들여쓰기 등)

(2) 예외 exception

(3) 예시

에러

- invalid syntax 

- assign to literal #잘못된 할당

- EOL(end of line) 마무리를 안지음

- EOF(end of file)

- 그외 다 예외

 

예외

- 프로그램 실행 중에 감지되는 에러 

*내장 예외 (Built - in Exceptions) : 예외 상황을 나타내는 클래스들

- ZeroDivisionError : 나누기 또는 모듈로 연산의 두번째 인자가 0발생

- NameError : 지역 또는 전역 이름 못찾았을 때

- TypeError : 타입불일치, 인자 초과, 인자 타입 불일치 등

- ValueError : 부적절한 값 인자를 받았고, 상황이 IndexError처럼 더 구체적 예외로 설명되지 않는 경우

- KeyError : 키가 존재하지 않을 때

 

 

3) 예외처리 사용 구문

- try : 예외가 발생할 수 있는 코드 작성 

- except : 예외가 발생했을 때 실행할 코드 작성

- else : 예외가 발생하지 않았을 때 실행할 코드 작성

- finally : 위쪽 코드(예외발생 여부)와 상관없이 항상 실행할 코드 작성 

try : 

except 예외 : #특정 예외에 적용 # 예외 안적음 모든 상황에 적용 #주의!

#예제
# try-except
try: 
    result = 10/0
except ZeroDivisionError:
    print('0으로 나눌 수 없습니다')

 

4) 복수 예외처리 

- 예제 : 100을 사용자가 입력한 값으로 나누고 출력

# 복수 예외처리
try:
    num = int(input('100을 나눌 값을 입력하시오'))
    print(100/num)
    
except (ValueError,ZeroDivisionError ) : #VALUEERROR : TYPE이 다를 때 예비
    print('제대로 입력해죠오')

 

 

5) 예외처리 주의사항 

- 상속(클래스) 안에서 이루어질 때 ... 부모 클래스 처리만 적용되고 하위 클래스 처리는 없는 게 되어버린다(상속  == 계층구조)

=> except 절로 분기시, 하위 예외 클래스부터 확인될 수 있게 작성해야함 (BaseException == 최상위 클래스 먼저 쓰지 않기)

 

 

6) 참고

-  as 키워드 : 예외 발생시 예외에 대한 정보를 담고 있는 객체 

except IndexError as error:
	print(f'{error}' 발생')

 

 

7) 예외처리와 값 검사에 대한 두가지 방식

- EAFP : 일단 지른다. 그다음에 처리하겠다 :  try-except 중심의 코드를 말함

- LBYL : 지르기 전에 확인해라(if-else) : 미리 방지하고 싶을 때 쓰는 코드 

- 그럼 각각 언제쓸까? 뭘 검사를 해야할지 모를때 EAFP를 써봐

- 일반적 상황에선 LBYL이긴 함(어느 게 더 낫다X)


담임쌤 보충수업

1) 개념

- 객체 : 사물이 가진 행동, 특징 등 

-클래스 : 객체를 표현하기 위한 방법

-인스턴스 변수 => '속성'이라 더 표현함

 

2) 주의

- 다중 상속 : 충돌 위험이 있다

 

3) 해시값

   #해시값 : 문자열이던 데이터에 대해 고유한 숫자를 만들어내는 것
        #문자열 주소값, 인코딩 등 기반 개발자별 만듦
        #데이터 같은지 비교할 때 해시값 비교 후 실제 값 같은지 더 빠르게 탐색법
        #빠른 검색을 위해 필요

 

4) mro 함수

- mro 순서 말고 내가 부른 순서로 호출하려면

class D(B, C):
    def __init__(self):
        #MRO 순서 X 내가 부른 순서
        C.__init__(self) # 클래스 C 먼저 호출
        B.__init__(self) # 클래스 B 다음 호출
        print('D Constructor') #자기자신 (D) 호출