01_Python

45_클래스_심화

chuuvelop 2025. 1. 10. 22:47
728x90
01. 소멸자(destructor)
  • 인스턴스가 소멸될 때 자동으로 호출되는 메서드 ##인스턴스(어느 클래스의 객체)==객체(클래스에서뽑아져나오는 덩어리)
class Sample:
    def __del__(self):
        print("인스턴스가 소멸됩니다")
sample_instance = Sample()
del sample_instance
인스턴스가 소멸됩니다

 

02. isinstance()
  • 객체가 어떤 클래스로부터 만들어졌는지 확인할 수 있는 함수
  • 첫 번째 매개변수에 객체, 두 번째 매개변수에 클래스를 입력
    • isinstance(객체, 클래스)
  • 객체가 해당 클래스를 기반으로 만들어졌으면 True, 관계 없으면 False를 반환
class Student:
    def __init__(self):
        pass
student = Student()
isinstance(student, Student)
True

 

  • student는 Student클래스를 기반으로 만들었으므로 True
  • 리스트 내부에 여러 종류의 객체가 들어있을 때 인스턴스들을 구분하며 속성과 기능을 사용할 때 사용
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()

 

공부를 합니다
공부를 합니다
학생을 가르칩니다
공부를 합니다
공부를 합니다

 

특수한 이름의 메서드

  • __<이름>__() 형태의 메서드들은 특수한 상황에 자동으로 호출되도록 만들어짐

※마크다운에서 __는 볼드이므로 역슬래시로 이스케이프 해줘야함

  • 파이썬이 클래스를 사용할 때 제공해주는 보조 기능
03. __str__()
  • str() 함수의 매개변수로 객체를 넣으면 호출되는 메소드
  • 객체를 문자열로 변환
class Student:
    def __init__(self, name, korean, math, english, science):
        self.name = name
        self.korean = korean
        self.math = math
        self.english = english
        self.science = science

    def get_sum(self):
        return self.korean + self.math + self.english + self.science

    def get_average(self):
        return self.get_sum() / 4

    def __str__(self):
        return f"{self.name}\t{self.get_sum()}\t{self.get_average()}"
students = [Student("윤인성", 87, 98, 88, 95),
           Student("연하진", 92, 98, 96, 98),
           Student("구지연", 76, 96, 94, 90)]

 

print("이름", "총점", "평균", sep = "\t")  ##sep:seperator(구분자의 초기값은 " ")

for i in students:
    print(str(i))
이름	총점	평균
윤인성	368	92.0
연하진	384	96.0
구지연	356	89.0

 

 

04. 크기 비교 메서드
이름 영어 설명
eq equal 같다
ne not equal 다르다
gt grater than 크다
ge grater than or equal 크거나 같다
lt less than 작다
le less than or equal 작거나 같다

 

class Student:
    def __init__(self, name, korean, math, english, science):
        self.name = name
        self.korean = korean
        self.math = math
        self.english = english
        self.science = science

    def get_sum(self):
        return self.korean + self.math + self.english + self.science

    def get_average(self):
        return self.get_sum() / 4

    def __str__(self):
        return f"{self.name}\t{self.get_sum()}\t{self.get_average()}"

    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 __lt__(self, value):
        return self.get_sum() < value.get_sum()

    def __le__(self, value):
        return self.get_sum() <= value.get_sum()

 

student_a = Student("나선주", 98, 92, 96, 92)
student_b = Student("윤아린", 95, 98, 98, 98)
student_a == student_b
False

 

student_a != student_b
True

 

# student_a.__gt__(student_b)
student_a > student_b
False

 

student_a < student_b
True

 

 

  • 비교할 때 사용하는 자료형을 한정하고 싶다면 예외발생을 활용할 수도 있음
student_a < 100

 

class Student:
    def __init__(self, name, korean, math, english, science):
        self.name = name
        self.korean = korean
        self.math = math
        self.english = english
        self.science = science

    def get_sum(self):
        return self.korean + self.math + self.english + self.science

    def get_average(self):
        return self.get_sum() / 4

    def __str__(self):
        return f"{self.name}\t{self.get_sum()}\t{self.get_average()}"

    def __eq__(self, value):
        if not isinstance(value, Student):
            raise TypeError("Student 클래스의 인스턴스만 비교할 수 있습니다")
        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 __lt__(self, value):
        return self.get_sum() < value.get_sum()

    def __le__(self, value):
        return self.get_sum() <= value.get_sum()

 

student_a = Student("나선주", 98, 92, 96, 92)
student_a == 10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[39], line 1
----> 1 student_a == 10

Cell In[37], line 20, in Student.__eq__(self, value)
     18 def __eq__(self, value):
     19     if not isinstance(value, Student):
---> 20         raise TypeError("Student 클래스의 인스턴스만 비교할 수 있습니다")
     21     return self.get_sum() == value.get_sum()

TypeError: Student 클래스의 인스턴스만 비교할 수 있습니다

 

 

05. 클래스 변수와 클래스 메소드

  • 객체가 변수와 메소드를 가지는 것 처럼 클래스도 변수와 메소드를 가질 수 있음
class Korean:
    country = "한국" # 클래스 변수 country

    def __init__(self, name, age, address):
        country = "한국"
        self.name = name # 인스턴스 변수 self.name
        self.age = age
        self.address = address
man = Korean("홍길동", 35, "서울")
man.name
'홍길동'

 

Korean.name   ###클래스는 인스턴스변수에 접근할 수 없다  ##인스턴스는 클래스에 접근할 수 있다(전역변수와 지역변수처럼)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[48], line 1
----> 1 Korean.name

AttributeError: type object 'Korean' has no attribute 'name'

 

man.country
'한국'

 

Korean.country
'한국'

 

 

class Person:

    bag = []

    def put_bag(self, stuff):
        Person.bag.append(stuff)

        #self.bag을 사용하면 인스턴스 변수와 혼동하기 쉽기 때문에 클래스 이름을 사용하는것을 권장
        #self.bag.append(stuff) 상기와 같은 코드
james = Person()
james.put_bag("책")

maria = Person()
maria.put_bag("열쇠")
print(james.bag)
print(maria.bag)
print(Person.bag)
['책', '열쇠']
['책', '열쇠']
['책', '열쇠']

 

class Person:

    bag = []

    def __init__(self):
        self.bag = []

    def put_bag(self, stuff):
        Person.bag.append(stuff) #클래스변수의 bag

    def put_mybag(self, stuff):
        self.bag.append(stuff) # 인스턴트 변수(self)의 bag
james = Person()
james.put_mybag("책")
james.put_bag("사전")
maria = Person()
maria.put_mybag("열쇠")
maria.put_bag("자물쇠")
# 같은 이름의 인스턴스 변수와 클래스 변수가 존재할 때는 인스턴스 변수 우선
print(james.bag)
print(maria.bag)
print(Person.bag)
['책']
['열쇠']
['사전', '자물쇠']

 

클래스 메서드

  • 인스턴스 또는 클래스로 호출
  • 생성된 인스턴스가 없어도 호출 가능
  • @classmethod 데코레이터를 표시하고 작성
  • 매개변수 cls를 사용
  • 클래스.메소드()의 형태로 사용
  • 인스턴스 변수에 접근할 수 없지만 클래스 변수에는 접근 가능
    • 메서드의 실행이 외부상태에 영향을 미치지 않는 순수함수를 만들 때
    • 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때
class Korean:

    country = "한국"

    @classmethod
    def trip(cls, country):
        if cls.country == country:
            print("국내여행")
    
        else:
            print("해외여행")
Korean.trip("한국")
국내여행
Korean.trip("미국")
해외여행

 

 

가비지 컬렉터(garbage collector)

  • 프로그램 내부에서 무언가 생성한다는 것은 메모리에 올린다는 의미
    • 메모리가 부족해지면 컴퓨터는 하드디스크를 메모리처럼 사용
      • 이런 동작을 스왑(swap)이라고 함
      • 하드디스크는 메모리보다 훨씬 느리기 때문에 스왑을 처리하는 속도도 느림
    • 프로그램에서 변수를 만들면 메모리에 데이터가 올라가고, 계속 만들게 되면 메모리가 가득 참
      • 파이썬에서는 가비지 컬렉터가 더 사용할 가능성이 없는 데이터(변수에 저장되지 않은 데이터)를 메모리에서 제거해서 메모리를 정리

 

# 변수에 저장하지 않은 경우
class Test:
    def __init__(self, name):
        self.name = name
        print(f"{self.name} - 생성되었습니다")

    def __del__(self):
        print(f"{self.name} - 파괴되었습니다")
Test("A")
Test("B")
Test("C")
A - 생성되었습니다
A - 파괴되었습니다
B - 생성되었습니다
B - 파괴되었습니다
C - 생성되었습니다
[43]:
<__main__.Test at 0x216888dbfe0>

 

 

# 변수에 저장한 경우
a = Test("A")
b = Test("B")
c = Test("C")
A - 생성되었습니다
A - 파괴되었습니다
B - 생성되었습니다
B - 파괴되었습니다
C - 생성되었습니다
  • 변수에 저장했으면 나중에 활용한다는 의미이므로 프로그램이 종료되는 순간까지 메모리에서 제거되지 않음

프라이빗 변수

  • 클래스 내부의 변수를 외부에서 사용하는 것을 막고 싶을 때 사용
  • 표현법
    • __변수이름
class Person:
    def __init__(self, name, age, address):
        self.hello = "안녕하세요"
        self.name = name
        self.age = age
        self.address = address
maria = Person("마리아", 20, "서울시 마포구")
print(maria.name)
마리아
class Person:
    def __init__(self, name, age, address, wallet):
        self.hello = "안녕하세요"
        self.name = name
        self.age = age
        self.address = address
        self.__wallet = wallet
maria = Person("마리아", 20, "서울시 마포구", 10000)
print(maria.__wallet) # 클래스 바깥에서 비공개 속성에 접근하면 에러가 발생
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[31], line 1
----> 1 print(maria.__wallet)

AttributeError: 'Person' object has no attribute '__wallet'
class Person:
    def __init__(self, name, age, address, wallet):
        self.hello = "안녕하세요"
        self.name = name
        self.age = age
        self.address = address
        self.__wallet = wallet

    def pay(self, amount):
        self.__wallet -= amount #비공개 속성은 클래스 안의 메서드에서만 접근할 수 있음
        print(f"{self.__wallet}원 남음")

 

maria = Person("마리아", 20, "서울시 마포구", 10000)
maria.pay(3000)
7000원 남음
maria.__wallet -= 3000
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[35], line 1
----> 1 maria.__wallet -= 3000

AttributeError: 'Person' object has no attribute '__wallet'

 

print(maria.hello)
안녕하세요
maria.hello = "안녕하세요!!"
print(maria.hello)
안녕하세요!!

 

 

게터(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)

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

    def set_radius(self, value):
        self.__radius = value

 

c = Circle(10)
print(c.get_circumference())
print(c.get_area())
print(c.get_radius())
62.83185307179586
314.1592653589793
10

 

c.set_radius(2)
print(c.get_circumference())
print(c.get_area())
print(c.get_radius())

 

12.566370614359172
12.566370614359172
2
함수를 사용해 값을 변경하게 하면 여러가지 추가 처리를 할 수 있음

         예) 반지름의 값을 양의 숫자로만 한정

 

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):
        if value <= 0:
            raise TypeError("반지름은 양의 숫자여야 합니다")
        self.__radius = value

 

c = Circle(10)

c.set_radius(-2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[51], line 3
      1 c = Circle(10)
----> 3 c.set_radius(-2)

Cell In[50], line 19, in Circle.set_radius(self, value)
     17 def set_radius(self, value):
     18     if value <= 0:
---> 19         raise TypeError("반지름은 양의 숫자여야 합니다")
     20     self.__radius = value

TypeError: 반지름은 양의 숫자여야 합니다

 

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

  • 게터와 세터를 함수로 만드는 일이 많아져서 게터와 세터를 쉽게 만들 수 있는 기능을 제공
  • 표현법
    • @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

 

c = Circle(10)
print("원래 반지름", c.radius) ##게터함수명으로 프라이빗 변수에 접근

c.radius = 2 ##세터에 들어감
print("변경된 반지름", c.radius)
원래 반지름 10
변경된 반지름 2
c.radius = -10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[57], line 1
----> 1 c.radius = -10

Cell In[54], line 21, in Circle.radius(self, value)
     18 @radius.setter
     19 def radius(self, value):
     20     if value <= 0:
---> 21         raise TypeError("반지름은 양의 숫자여야 합니다")
     22     self.__radius = value

TypeError: 반지름은 양의 숫자여야 합니다
  • 데코레이터를 이용하면 객체.변수 를 사용하는 것 만으로 자동으로 게터와 세터가 호출되도록 할 수 있음

 

상속(inheritance)

  • 다른 누군가가 만들어 놓은 기본 형태에 내가 원하는 것만 추가하거나 교체하는 것
  • 다중 상속
    • 다른 누군가가 만들어 놓은 형태들을 조립해서 내가 원하는 것을 만드는 것
  • 기반이 되는 것을 부모(parent)라고 부르고, 이를 기반으로 생성된 것을 자식child)이라고 부름
    • 부모가 자식에게 자신의 기반을 물려주는 것이기 때문에 상속이라고 부름
class Person: # 부모 클래스
    def __init__(self, name):
        self.name =name

    def eat(self, food):
        print(self.name + "가 " + food + "를 먹습니다")
class Student(Person): #자식클래스
    def __init__(self, name, school):
        super().__init__(name)
        self.school = school

    def study(self):
        print(self.name + "는 " + self.school + "에서 공부합니다")
potter = Student("해리포터", "호그와트")
potter.eat("감자")
해리포터가 감자를 먹습니다
potter.study()
해리포터는 호그와트에서 공부합니다

 

# 부모 클래스 선언
class Parent:
    def __init__(self):
        self.value = "테스트"
        print("Parent클래스의 생성자가 호출되었습니다")

    def test(self):
        print("Parent 클래스의 test() 메서드입니다")

 

# 자식 클래스의 선언
class Child(Parent):
    def __init__(self):
        super().__init__() #부모의 생성자 호출
        print("Child 클래스의 생성자가 호출되었습니다")
child = Child()
child.test()
print(child.value)
Parent클래스의 생성자가 호출되었습니다
Child 클래스의 생성자가 호출되었습니다
Parent 클래스의 test() 메서드입니다
테스트

 

 

자식 클래스의 생성자

  • 자식 클래스는 부모 클래스가 없으면 존재할 수 없음
  • 따라서 자식 클래스의 생성자를 구현할 때는 반드시 부모 클래스의 생성자를 먼저 호출해야함
  • 자식 클래스에서 생성자를 생략한다면 부모 클래스의 생성자가 자동으로 호출되기 때문에 super()를 사용하지 않아도 됨
class Person:
    def __init__(self):
        self.hello = "안녕하세요"
class Student(Person):
    pass
james = Student()
print(james.hello)
안녕하세요

 

자식 클래스의 인스턴스 자료형

  • 자식 클래스 객체는 자식 클래스의 객체임과 동시에 부모 클래스의 객체
print(isinstance(james, Student))
print(isinstance(james, Person))
True
True

 

클래스 상속의 활용

  • 부모에 정의되어 있는 함수를 자식에서 다시 정의하는 것을 재정의 또는 오버라이드(override)라고 부름
  • 기존 함수/변수 이외의 것을 완전히 새로 정의하는 것도 가능
class Person:
    def greeting(self):
        print("안녕하세요")
class Student(Person):
    def greeting(self):
        print("안녕하세요. 인스턴스 메서드 입니다.")
james = Student()
james.greeting()
안녕하세요. 인스턴스 메서드 입니다.
class Student(Person):
    def greeting(self):
        super().greeting() #부모 클래스의 메서드를 이용하여 중복을 줄임
        print("인스턴스 메서드입니다.")
james = Student()
james.greeting()
안녕하세요
인스턴스 메서드입니다.

 

다중 상속

class Person:
    def greeting(self):
        print("안녕하세요")
class University:
    def manage_credit(self):
        print("학점 관리")
class Undergraduate(Person, University):
    def study(self):
        print("공부하기")
james = Undergraduate()
james.greeting()
james.manage_credit()
james.study()
안녕하세요
학점 관리
공부하기
728x90

'01_Python' 카테고리의 다른 글

47_모듈  (0) 2025.01.14
46_Visual Studio Code설치(VS Code)  (0) 2025.01.13
44_클래스  (1) 2025.01.10
43_파이썬 내장함수  (0) 2025.01.09
42_데이터 입출력  (1) 2025.01.09