본문 바로가기
개발자모드/혼자공부하는파이썬

[파이썬#24][고급] 클래스의 추가적인 구문 - isinstance(), 클래스 변수와 함수, 상속, 가비지컬렉터, 게터와 세터 ★혼공파 최종편★

by 요니L 2022. 7. 28.

 

 

파이썬은 클래스의 부가적인 기능을 지원한다. 예를 들어 어떤 클래스를 기반으로 그 속성과 기능을 물려받아 새로운 클래스를 만드는 상속, 이러한 상속 관계에 따라서 객체가 어떤 클래스를 기반으로 만들었는지 확인할 수 있게 해주는 isinstance() 함수, 파이썬이 기본적으로 제공하는 str() 함수 혹은 연산자를 사용하여 클래스의 특정 함수를 호출할 수 있게 해주는 기능 등이 대표적인 예이다.  


어떤 클래스의 인스턴스인지 확인

객체(인스턴스)가 어떤 클래스로부터 만들어졌는지 확인할 수 있도록 isinstance() 함수를 제공한다. 이때 인스턴스가 해당 클래스로 만들어졌다면 True, 전혀 상관이 없는 인스턴스와 클래스라면 False를 리턴한다. 

isinstance(인스턴스, 클래스)

 

▷ 간단한 예

# 클래스 선언
class Student:

    def __init__(self):
        pass

# 핵생을 선언
stdt = Student()

# 인스턴스 확인
print(isinstance(stdt, Student))

[실행결과]

PS D:\app\python> python test.py
True
PS D:\app\python> 

 

▷ 단순한 인스턴스 확인

# 클래스 선언
class Student:

    def __init__(self):
        pass

# 핵생을 선언
stdt = Student()

if type(stdt) == Student:
    print("stdt는 Student 클래스 기반으로 만들어 졌다.")
else:
    print("stdt는 Student 클래스 기반으로 만들어 지지 않았다.")

[실행결과]

PS D:\app\python> python test.py
stdt는 Student 클래스 기반으로 만들어 졌다.
PS D:\app\python> 

 

이 방법은 상속을 사용할 때는 다르게 동작한다. 아래 코드를 살펴보자.

 

▷ isinstance() 함수와 type()함수로 확인하는 차이

# 클래스 선언
class Human:
    def __init__(self):
        pass

# Human을 상속받아 Student클래스 선언
class Student(Human):
    def __init__(self):
        pass

# 핵생을 선언
stdt = Student()

# 인스턴스 확인
print("ulog isinstance(stdt, Human): " , isinstance(stdt, Human))
print("ulog type(stdt)== Human: " , type(stdt)== Human)

[실행결과]

PS D:\app\python> python isinst_type.py
ulog isinstance(stdt, Human):  True
ulog type(stdt)== Human:  False

Student 클래스는 Human 클래스를 상속받아서 만들어졌다. isinstance() 함수는 이러한 상속 관계까지 확인하지만 type() 함수는 상속 관계까지 확인하지 않는다. 

 

 

▷ isinstance() 함수 활용

Student와 Teacher라는 클래스를 생성하고  classroom이라는 리스트 내부에 객체들을 넣었다. 그리고 반복을 적용했을 때 요소가 Student 클래스의 인스턴스인지, Teacher 클래스의 인스턴스인지 확인하고, 각각의 대상이 가지고 있는 적절한 함수를 호출한다. 

# 클래스 선언
class Student:
    def study(self):
        print("공부를 합니다.")

class Teacher:
    def teach(self):
        print("학생을 가르칩니다.")        

# 교실 내부의 객체 리스트를 생성
classroom = [Student(), Student(), Teacher(), Student(), Student()]

# 반복을 적용해서 적절한 함수를 호출하게 한다.
for person in classroom:
    if isinstance(person,Student):
        person.study()
    elif isinstance(person,Teacher):
        person.teach()

[실행결과]

PS D:\app\python> python isinstance.py
공부를 합니다.
공부를 합니다.
학생을 가르칩니다.
공부를 합니다.
공부를 합니다.
PS D:\app\python>

 

 


특수한 이름의 메소드

우리가 만든 Student 클래스를 기반으로 객체를 만들고 객체 뒤에 [.]를 입력해서 자동 완성 기능(비주얼 스튜디오 코드)을 살펴보면 우리가 만들지 않았던 함수들이 잔뜩 들어 있는 것을 확인할 수 있다. 이는 모두 파이썬이 클래스를 사용할 때 제공해 주는 보조 기능이다. 그런데 이름들이 조금 특이하다 __<이름>__() 형태로 되어 있다. 이러한 메소드는 특수한 상황에 자동으로 호출되도록 만들어졌다. 

 

▷ __str__() 함수

우선 다음과 같이 __str__()을 클래스 내부에 정의해 보자. 이렇게 __str__() 함수를 정의하면 str() 함수를 호출할 때 __str__() 함수가 자동을 호출된다.

 

# 클래스를 선언
class Student:
    def __init__(self, name, korean, english, science):
        self.name = name
        self.korean = korean
        self.english = english
        self.science = science
   
    def get_sum(self):
        return self.korean + self.korean + self.english + self.science

    def get_avg(self):
        return self.get_sum() / 3

    def __str__(self):
        return "{}\t{}\t{}".format(
            self.name,
            self.get_sum(),
            self.get_avg()
        )

# 학생 리스트를 선언
students = [
    Student("윤인성", 87, 98, 88),
    Student("연하진", 95, 98, 94),
    Student("구지연", 76, 96, 98),
    Student("나선주", 98, 92, 92),
    Student("윤아린", 64, 98, 92)
]

# 출력
print("이름", "총점", "평균", sep="\t")
for student in students:
    print(str(student))

[실행결과]

PS D:\app\python> python str_func.py  
이름    총점    평균
윤인성  360     120.0
연하진  382     127.33333333333333
구지연  346     115.33333333333333
나선주  380     126.66666666666667
윤아린  318     106.0
PS D:\app\python>

str() 함수의 매개변수로 넣으면 student의 __str__() 함수가 호출된다. 

 

▷ 크기 비교 함수

몇 가지 특이한 이름을 정리해 보면 다음과 같다. 다음은 크기 비교 함수 이름이다.

이름 영어 설명
eq equal 같다
ne not equal 다르다
gt greater than 크다
ge greater than or equal 크거나 같다
lt less than 작다
le less  than or equal 작거나 같다.

이를 활용해서 학생들을 성적으로 비교할 수 있도록 만들어 보자. 

 

# 클래스를 선언
class Student:
    def __init__(self, name, korean, english, science):
        self.name = name
        self.korean = korean
        self.english = english
        self.science = science
   
    def get_sum(self):
        return self.korean + self.korean + self.english + self.science

    def get_avg(self):
        return self.get_sum() / 3

    def __str__(self):
        return "{}\t{}\t{}".format(
            self.name,
            self.get_sum(),
            self.get_avg()
        )

    def __eq__(self, value):
        return self.get_sum() == value.get_sum()
    def __ne__(self, value):
        return self.get_sum() != value.get_sum()
    def __gt__(self, value):
        return self.get_sum() > value.get_sum()
    def __ge__(self, value):
        return self.get_sum() >= value.get_sum()
    def __lt__(self, value):
        return self.get_sum() < value.get_sum()
    def __le__(self, value):
        return self.get_sum() <= value.get_sum()                                        

# 학생 리스트를 선언
students = [
    Student("윤인성", 87, 98, 88),
    Student("연하진", 95, 98, 94),
    Student("구지연", 76, 96, 98),
    Student("나선주", 98, 92, 92),
    Student("윤아린", 64, 98, 92)
]

# 학생을 선언
student_a = Student("윤인성", 87, 98, 88)
student_b = Student("연하진", 95, 98, 94)

# 출력
print("student_a == student_b => ", student_a == student_b)
print("student_a != student_b => ", student_a != student_b)
print("student_a > student_b  => ", student_a > student_b)
print("student_a >= student_b => ", student_a >= student_b)
print("student_a < student_b  => ", student_a < student_b)
print("student_a <= student_b => ", student_a <= student_b)

[실행결과]

PS D:\app\python> python compare_func.py
student_a == student_b =>  False
student_a != student_b =>  True
student_a > student_b  =>  False
student_a >= student_b =>  False
student_a < student_b  =>  True
student_a <= student_b =>  True
PS D:\app\python>

이렇게 제공되는 함수는 객체를 다룰 수 있게 해주는 함수이다. 프로그래밍할 때는 항상 다른 사람과 함께 한다는 협업의 관점에서 이러한 함수를 사용하는 것이 좋다. 

 

 

 


클래스 변수와 메소드

인스턴스가 속성과 기능을 가질 수도 있지만, 클래스가 속성(변수)과 기능(함수)를 가질 수도 있다.

 

 

↘클래스 변수

클래스 변수를 만드는 방법부터 살펴보자. 클래스 변수는 class 구문 바로 아래의 단계에 변수를 선언하기만 하면 된다.

 

#클래스 변수 만들기
class 클래스 이름:
    클래스 변수 =

#클래스 변수에 접근하기
클래스 이름.변수 이름

 

▷ 클래스 변수

# 클래스를 선언
class Student:
    count = 0

    def __init__(self, name, korean, english, science):
        #인스턴스 변수 초기화
        self.name = name
        self.korean = korean
        self.english = english
        self.science = science

        # 클래스 변수 설정
        Student.count +=1
        print("{}번째 학생이 생성되었습니다.".format(Student.count))
   
   
# 학생 리스트를 선언
students = [
    Student("윤인성", 87, 98, 88),
    Student("연하진", 95, 98, 94),
    Student("구지연", 76, 96, 98),
    Student("나선주", 98, 92, 92),
    Student("윤아린", 64, 98, 92)
]

# 출력
print()
print("현재 생성된 총 학생 수는 {}명입니다.".format(Student.count))

[실행결과]

PS D:\app\python> python class_var.py
1번째 학생이 생성되었습니다.
2번째 학생이 생성되었습니다.
3번째 학생이 생성되었습니다.
4번째 학생이 생성되었습니다.
5번째 학생이 생성되었습니다.

현재 생성된 총 학생 수는 5명입니다.
PS D:\app\python>

 

 

↘클래스 함수

클래스 함수도 클래스 변수처럼 그냥 클래스가 가진 함수이다. 일반 함수로 만드나 클래스 함수로 만드나 사용에는 큰 차이가 없다. 그런데 생성하는 방법이 조금 특이하다. @classmethod 부분을 데코레이터라고 부른다. 

 

# 클래스 함수 만들기
class 클래스 이름:
    @classmethod
    def 클래스 함수(cls, 매개변수) #cls는 클래스라고 읽으면 된다.
        pass

# 클래스 함수 호출하기
클래스 이름.함수이름(매개변수)

 

▷ 클래스 함수

# 클래스를 선언
class Student:
    # 클래스 변수
    count = 0
    students = []

    # 클래스 함수
    @classmethod
    def print(cls):
        print("---------- 학생 목록 ----------")
        print("이름\t총점\t평균")
        for student in cls.students:
            print(str(student))
        print("-------------------------------")

    # 인스턴스 함수
    def __init__(self, name, korean, english, science):
        #인스턴스 변수 초기화
        self.name = name
        self.korean = korean
        self.english = english
        self.science = science

        # 클래스 변수 설정
        Student.count +=1
        Student.students.append(self)
   
    def get_sum(self):
        return self.korean + self.korean + self.english + self.science

    def get_avg(self):
        return self.get_sum() / 3

    def __str__(self):
        return "{}\t{}\t{}".format(
            self.name,
            self.get_sum(),
            self.get_avg()
        )
   
# 학생 리스트를 선언
Student("윤인성", 87, 98, 88)
Student("연하진", 95, 98, 94)
Student("구지연", 76, 96, 98)
Student("나선주", 98, 92, 92)
Student("윤아린", 64, 98, 92)

# 출력
print()
Student.print()

[실행결과]

PS D:\app\python> python class_func.py

---------- 학생 목록 ----------
이름    총점    평균
윤인성  360     120.0
연하진  382     127.33333333333333
구지연  346     115.33333333333333
나선주  380     126.66666666666667
윤아린  318     106.0
-------------------------------
PS D:\app\python>

 

 

 

 


(좀 더 알아보기1) 가비지 컬렉터

자주 언급되는 개념 중의 하나가 가비지 컬렉터(garbage collector)이다. 프로그램 내부에서 무언가를 생성한다는 것은 메모리 위에 올린다는 의미이다. 메모리가 부족해지면 컴퓨터는 하드디스크를 메모리처럼 사용해 무언가를 올리기 시작하는데 이러한 동작을 스왑이라고 하는데, 하드디스크는 메모리보다 훨씬 느리므로 스왑을 처리하는 속도도 느리다.

 

프로그램에서 변수를 만들면 데이터가 메모리에 올라가고, 계속 만들다 보면 분명 메모리가 가득 차버릴 것이다. 하지만 파이썬 언어에는 가비지 컬렉터라는 것이 있다. 가비지 컬렉터는 더 사용할 가능성이 없는 데이터를 메모리에서 제거하는 역할을 한다.

 

 

▷ 가비지 컬렉터: 변수에 저장하지 않는 경우

class Test:
    def __init__(self, name):
        self.name = name
        print("{} - 생성되었습니다.".format(self.name))
    def __del__(self):
        print("{} - 파괴되었습니다.".format(self.name))

Test("A")
Test("B")
Test("C")

[실행결과]

PS D:\app\python> python garbage01.py
A - 생성되었습니다.
A - 파괴되었습니다.
B - 생성되었습니다.
B - 파괴되었습니다.
C - 생성되었습니다.
C - 파괴되었습니다.

A가 생성되고 다음 줄로 넘어갈 때 이것을 변수에 저장하지 않으면 가비지 컬렉터는 이후에 활용하지 않겠다는 의미로 이해하고 메모리를 아끼기 위해 과감히 지워 버린다. 따라서 A가 생성되고, 사용되지 않을 것이 확실하므로 A를 제거해서 소멸한다. 이러한 과정이 반복되는 것이다.

 

가비지 컬렉터: 변수에 저장한 경우

class Test:
    def __init__(self, name):
        self.name = name
        print("{} - 생성되었습니다.".format(self.name))
    def __del__(self):
        print("{} - 파괴되었습니다.".format(self.name))
a = Test("A")
b = Test("B")
c = Test("C")

[실행결과]

PS D:\app\python> python garbage02.py
A - 생성되었습니다.
B - 생성되었습니다.
C - 생성되었습니다.
A - 파괴되었습니다.
B - 파괴되었습니다.
C - 파괴되었습니다.
PS D:\app\python>

가비지 컬렉터는 '이거 변수에 저장했으면 나중에 활용한다는 의미가 아닐까? 좀 더 지켜보자!' 라고 생각하고 프로그램이 종료되는 순간까지 데이터를 메모리에서 제거하지 않는다. 

 

 


(좀 더 알아보기2) 프라이빗 변수와 게터/세터 (getter/setter)

객체 지향 프로그래밍의 최종 목표는 객체를 효율적으로 만들고 사용하는 것이다. 객체를 효율적으로 사용하기 위한 추가 기능을 알아보기 위해 간단한 예를 살펴보자.

 

▷ 원의 둘레와 넓이를 구하는 객체 지향 프로그램

# 모듈을 가져온다.
import math

# 클래스를 선언
class Circle:
    def __init__(self, radius):
        self.radius = radius
    def get_circumference(self):
        return 2 * math.pi * self.radius
    def get_area(self):
        return math.pi * (self.radius ** 2)

# 원의 둘레와 넓이를 구함
circle = Circle(10)
print("원의 둘레:", circle.get_circumference())
print("원의 넓이:", circle.get_area())

[실행결과]

PS D:\app\python> python math_sample.py
원의 둘레: 62.83185307179586
원의 넓이: 314.1592653589793
PS D:\app\python>

 

만약 Circle 클래스의 radius 속성에 음수를 넣게 되면 둘레는 음수가 나온다. 현실에서 길이가 음수가 될 수는 없다. 따라서 길이를 음수로 넣는 것을 막는 방법이 필요하다. 

 

↘프라이빗 변수

일단 변수를 마음대로 사용하는 것을 막아야 한다. 파이썬은 클래스 내부의 변수를 외부에서 사용하는 것을 막고 싶을 때는 인스턴스 변수 이름을 __<변수 이름> 형태로 선언한다.

 

 

▷ 프라이빗 변수

# 모듈을 가져온다.
import math

# 클래스를 선언
class Circle:
    def __init__(self, radius):
        self.__radius = radius
    def get_circumference(self):
        return 2 * math.pi * self.__radius
    def get_area(self):
        return math.pi * (self.__radius ** 2)

# 원의 둘레와 넓이를 구함
circle = Circle(10)
print("원의 둘레:", circle.get_circumference())
print("원의 넓이:", circle.get_area())
print()

#__radisu에 접근
print("# __radius에 접근합니다.")
print(circle.__radius)

[실행결과]

PS D:\app\python> python private_var.py
원의 둘레: 62.83185307179586
원의 넓이: 314.1592653589793

# __radius에 접근합니다.
Traceback (most recent call last):
  File "D:\app\python\private_var.py", line 21, in <module>
    print(circle.__radius)
AttributeError: 'Circle' object has no attribute '__radius'
PS D:\app\python>

클래스 내부에서 __radius를 사용하는 것은 아무 문제없지만, 클래스 외부에서 __radius를 사용하려고 할 때 그런 속성이 없다는 오류를 출력한다. 이처럼 속성을 선언할 때 앞에 __를 붙이기만 하면 외부에서 사용할 수 없는 변수가 된다.

 

 

↘게터와 세터(getter and setter)

그런데 중간에 원의 둘레를 변경하고 싶다면 어떻게 해야 할까? 클래스 외부에서 직접 __radius 속성에 접근할 수 없기 때문에 간접적으로 접근할 수 있는 방법을 찾아야 한다. 이때 사용되는 것이 게터와 세터이다. 게터와 세터는 프라이빗 변수의 값을 추출하거나 변경할 목적으로, 간접적으로 속성에 접근하도록 해주는 함수이다. 

 

▷ 게터와 세터

# 모듈을 가져온다.
import math

# 클래스를 선언
class Circle:
    def __init__(self, radius):
        self.__radius = radius
    def get_circumference(self):
        return 2 * math.pi * self.__radius
    def get_area(self):
        return math.pi * (self.__radius ** 2)

    # 게터와 세터를 선언
    def get_radius(self):
        return self.__radius
    def set_radius(self, value):
        self.__radius = value

# 원의 둘레와 넓이를 구함
circle = Circle(10)
print("원의 둘레:", circle.get_circumference())
print("원의 넓이:", circle.get_area())
print()

# 간접적으로 __radisu에 접근
print("# 간접적으로__radius에 접근합니다.")
print(circle.get_radius())
print()

# 반지름 변경하고 원의 둘레와 넓이를 구함.
circle.set_radius(2)
print("원의 둘레:", circle.get_circumference())
print("원의 넓이:", circle.get_area())

[실행결과]

PS D:\app\python> python getter_setter.py
원의 둘레: 62.83185307179586
원의 넓이: 314.1592653589793

# 간접적으로__radius에 접근합니다.
10

원의 둘레: 12.566370614359172
원의 넓이: 12.566370614359172
PS D:\app\python>

get_radius() 함수와 set_radius() 함수를 만들어서 함수로 프라이빗 변수의 값에 접근하거나, 값을 변경하게 했다. 

 

이렇게 함수를 사용해 값을 변경하면 여러 가지 처리를 추가할 수 있다. 예를 들어 set_radius() 함수에 다음과 같은 코드를 추가하면 __radius에 할당할 값을 양수로만 한정할 수 있다. 

   def set_radius(self, value):
        if value <= 0:
            raise TypeError("길이는 양수여야 합니다.")
        self.__radius = value

 

↘데코레이터를 사용한 게터와 세터

게터와 세터를 함수로 만드는 일이 많아 파이썬 언어는 게터와 세터를 쉽게 만들고 사용할 수 있게 하는 기능을 제공한다. 다음과 같이 변수 이름과 같은 함수를 정의하고 위해 @property@<변수 이름>.setter라는 데코레이터를 붙여주면 된다.

 

▷ 데코레이터를 사용해 게터와 세터 만들기

# 모듈을 가져온다.
import math

# 클래스를 선언
class Circle:
    def __init__(self, radius):
        self.__radius = radius
    def get_circumference(self):
        return 2 * math.pi * self.__radius
    def get_area(self):
        return math.pi * (self.__radius ** 2)

    # 게터와 세터를 선언
    @property
    def radius(self):
        return self.__radius
    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise TypeError("길이는 양수여야 합니다.")
        self.__radius = value

# 원의 둘레와 넓이를 구함
print("# 데코레이터를 사용한 Getter와 Setter")
circle = Circle(10)
print("원의 반지름:", circle.radius)
circle.radius = 2
print("변경된 원의 반지름:", circle.radius)
print()

# 강제로 예외를 발생
print("# 강제로 예외를 발생시킵니다")
circle.radius = -10
print()

# 반지름 변경하고 원의 둘레와 넓이를 구함.
circle.set_radius(2)
print("원의 둘레:", circle.get_circumference())
print("원의 넓이:", circle.get_area())

[실행결과]

PS D:\app\python> python deco01.py
# 데코레이터를 사용한 Getter와 Setter
원의 반지름: 10
변경된 원의 반지름: 2

# 강제로 예외를 발생시킵니다
Traceback (most recent call last):
  File "D:\app\python\deco01.py", line 33, in <module>
    circle.radius = -10
  File "D:\app\python\deco01.py", line 20, in radius
    raise TypeError("길이는 양수여야 합니다.")
TypeError: 길이는 양수여야 합니다.
PS D:\app\python>

이렇게 하면 circle.radius를 사용하는 것만으로 자동으로 게터와 세터가 호출되도록 할 수 있다. 

 


(좀 더 알아보기3) 상속

클래스 기반의 객체 지향 언어들은 상속(inheritance)이라는 기능을 지원한다. 상속이라는 기능은 매우 고급 기술이라서 기본적인 내용을 알아보고, 예외 객체를 만들며 간단한 활용 예까지만 살펴보자.

 

 

↘상속

누군가가 만들어 놓은 기본 형태에 내가 원하는 것만 교체하는 것이 바로 상속이다.

 

↘다중 상속 

다른 누군가가 만들어 놓은 형태들을 조립해서 내가 원하는 것을 만드는 것을 다중 상속이라고 부른다.

 

프로그래밍 언어는 기반이 되는 것을 부모(parent)라고 부르고, 이를 기반으로 생성한 것을 자식(child)이라고 부른다. 부모가 자식에게 자신의 기반을 물려주는 기능이므로 '상속'이라고 부르는 것이다. 

 

 

▷ 상속의 활용

# 부모 클래스를 선언
class Parent:
    def __init__(self):
        self.value = "테스트"
        print("Parent 클래스의 __init__ 메소드가 호출되었입니다.")
    def test(selef):
        print("Parent 클래스의 test() 메소드입니다.")

class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        print("Child 클래스의 __init__ 메소드가 호출되었입니다.")


# 자식 클래스의 인스턴스를 생성하고 부모의 메소드를 호출한다.
child = Child()
child.test()
print(child.value)

[실행결과]

PS D:\app\python> python inherit01.py
Parent 클래스의 __init__ 메소드가 호출되었입니다.
Child 클래스의 __init__ 메소드가 호출되었입니다.
Parent 클래스의 test() 메소드입니다.
테스트
PS D:\app\python>

코드가 복잡해 보일 수 있는데, 이 전체가 상속하는 구문이므로 상속을 활용하고자 한다면 모두 외워라!

Child 클래스 내부에는 아무것도 없는데, Parent 클래스의 상속을 받았으므로 Parent 클래스가 가지고 있는 함수와 변수를 활용할 수 있는 것이다. 사실 이것만 보면 어떻게 활용해야 하는지 모를 수 있어서 상속을 활용하는 간단한 예를 살펴보자.

 

 

↘예외 클래스 만들기

상속은 기존에 있는 클래스를 기반으로 조금 수정해서 내가 원하는 클래스를 만들 때 사용한다. 그럼 Exception이라는 기존에 있는 클래스를 조금 수정해서 CustomException이라는 클래스를 만들어 보자.

 

▷ 사용자 정의 예외 클래스 만들기

class CustomException(Exception):
    def __init__(self):
        Exception.__init__(self)

raise CustomException

[실행결과]

PS D:\app\python> python inherit02.py
Traceback (most recent call last):
  File "D:\app\python\inherit02.py", line 5, in <module>
    raise CustomException
__main__.CustomException
PS D:\app\python>

Exception 클래스를 상속했으므로, Exception 클래스와 이름만 다르지 모두 같은 클래스이다. 따라서 raise로 예외를 발생시킬 수 있는 것이다. 

 

몇 가지 수정을 해보겠다. 일단 __init__() 내부에서 간단한 출력을 해보게 하고 __str__() 함수를 만들어 원하는 형태로 수정한다. 

 

 

▷ 자식 클래스로써 부모의 함수 재정의(오버라이드)하기

class CustomException(Exception):
    def __init__(self):
        Exception.__init__(self)
        print("#### 내가 만든 오류가 생성되었어요!")
    def __str__(self):
        return "오류가 발생했어요"

raise CustomException

[실행결과]

PS D:\app\python> python inherit03.py
#### 내가 만든 오류가 생성되었어요!
Traceback (most recent call last):
  File "D:\app\python\inherit03.py", line 8, in <module>
    raise CustomException
__main__.CustomException: 오류가 발생했어요
PS D:\app\python>

 

 

자식 클래스로써 부모에 없는 새로운 함수 정의하기

# 사용자 정의 예외를 생성
class CustomException(Exception):
    def __init__(self, message, value):
        Exception.__init__(self)
        self.message = message
        self.value = value
       
    def __str__(self):
        return self.message

    def print(self):
        print("#### 오류 정보 ####")
        print("메시지: ", self.message)
        print("값: ", self.value)

# 예외를 발생 시킴
try:
    raise CustomException("딱히 이유 없음", 273)
except CustomException as e:
    e.print()

[실행결과]

PS D:\app\python> python inherit04.py
#### 오류 정보 ####
메시지:  딱히 이유 없음
값:  273
PS D:\app\python>

 

 


연습문제

(연습문제 411p-1)

Q. Student 객체를 숫자와 비교했을 때 학생의 성적 평균과 비교가 일어나게 해보자.

# 클래스를 선언
class Student:
    def __init__(self, name, korean, english, science):
        self.name = name
        self.korean = korean
        self.english = english
        self.science = science
   
    def get_sum(self):
        return self.korean + self.english + self.science

    def get_avg(self):
        return self.get_sum() / 3

    def __eq__(self, value):
        return self.get_avg() == value
    def __ne__(self, value):
        return self.get_avg() != value
    def __gt__(self, value):
        return self.get_avg() > value
    def __ge__(self, value):
        return self.get_avg() >= value
    def __lt__(self, value):
        return self.get_avg() < value
    def __le__(self, value):
        return self.get_avg() <= value                                    

# 학생을 선언
test = Student("A", 90, 90, 90)

# 출력
print("test == 90 => ", test == 90)
print("test != 90 => ", test != 90)
print("test > 90  => ", test > 90)
print("test >= 90 => ", test >= 90)
print("test < 90  => ", test < 90)
print("test <= 90 => ", test <= 90)


[실행결과]

PS D:\app\python> python 411p-1.py
test == 90 =>  True
test != 90 =>  False
test > 90  =>  False
test >= 90 =>  True
test < 90  =>  False
test <= 90 =>  True
PS D:\app\python>

 

 

여기까지. 

 


 

에필로그

지금까지 혼공파 책을 통해 기초 문법에 대하여 공부했다. 파이썬을 공부해본 결과 파이썬만의 특징들은 있었지만 어떤 프로그램을 구현함에 있어서 다른 프로그래밍 언어와 큰 틀은 비슷한 것 같다. 파이썬 기초 문법을 끝냈으니 데이터 분석이나 django 프레임웍을 사용한 웹프로그래밍 쪽을 조금 더 공부해 보고 싶다!

댓글