본문 바로가기
프로그래밍 이야기/Python

[Python] type과 instance의 차이 그리고 type으로 클래스 메서드 사용하기

by meticulousdev 2022. 7. 18.
반응형

    어느 날 고전 컴퓨터 알고리즘 인 파이썬 책을 보는데 type(인스턴스 A).method_name(인스턴스 B) // (인스턴스 A, B는 동일 클래스로부터 생성)라고 작성된 코드를 발견하게 됩니다. 이게 대체 뭔가요? 클래스의 메서드를 단순히 호출하는 것도 아니고 type을 사용하고 메서드에 다른 클래스를 넣어서 실행을 한다고요? 이 글의 코드 몇 줄에 대한 궁금증으로 시작하여 type과 instance 그리고 type으로 클래스 메서드(class method)를 사용하는 방법에 대해서 공부하고 정리한 글입니다.

 

 

1. 클래스 그리고 상속

    간단한 예제를 만들어 보겠습니다. 클래스 상속에서 흔히 등장하는 컴퓨터, 데스크톱, 그리고 랩탑을 가지고 예시를 만들어 보겠습니다.

 

1) 부모 클래스인 Computer 클래스는 기본적으로 이름(self.name), cpu(self.cpu), gpu(self.gpu)에 대한 정보를 가지고 있다고 하겠습니다. 그리고 컴퓨터의 정보를 출력하는 get_computer_info 메서드가 있다고 하겠습니다.

 

2) Desktop 클래스는 Computer 클래스를 상속받습니다. 데스크톱이기 때문에 파워(power_name)에 대한 정보를 가지게 됩니다. 그리고 super().__init__을 통해서 초기화를 합니다. 그리고 데스크톱의 정보를 출력하는 get_deskop_info 메서드가 있다고 하겠습니다.  

 

3) 비슷한 방식으로 Laptop 클래스는 배터리 정보(battery_name)를 가지고 있으며, 랩탑의 정보를 출력할 수 있다고 하겠습니다.

 

class Computer:
    def __init__(self, name: str, cpu_name: str, gpu_name: str):
        self.name = name
        self.cpu_name = cpu_name
        self.gpu_name = gpu_name

    def get_computer_info(self):
        print(f"name    : {self.name}")
        print(f"cpu name: {self.cpu_name}")
        print(f"gpu name: {self.gpu_name}")


class Desktop(Computer):
    def __init__(self, name: str, cpu_name: str, gpu_name: str, power_name: str):
        super().__init__(name, cpu_name, gpu_name)
        self.power_name = power_name

    def get_desktop_info(self):
        super().get_computer_info()
        print(f"power info: {self.power_name}")


class Laptop(Computer):
    def __init__(self, name: str, cpu_name: str, gpu_name: str, battery_name: str):
        super().__init__(name, cpu_name, gpu_name)
        self.battery_name = battery_name

    def get_laptop_info(self):
        super().get_computer_info()
        print(f"battery info: {self.battery_name}")

 

2. type()과 isinstance() 비교

    그다음으로는 Laptop클래스를 가지고 2개의 인스턴스를 생성하고 인스턴스들의 정보를 확인해보겠습니다.

my_computer: Laptop = Laptop('AMD Laptop', 'AMD Rygen 5', 'Radeon', 'ABC Battery')
your_computer: Laptop = Laptop('Intel Laptop', 'Intel i5', 'RTX 3060', 'DEF Battery')

 

먼저 type을 확인해볼 경우 둘 다 Laptop 클래스의 인스턴스라고 나옵니다.

 

# code
print(f"type(my_computer)  : {type(my_computer)}")
print(f"type(your_computer): {type(your_computer)}")

# type(my_computer) : <class '__main__.Laptop'> 
# type(your_computer): <class '__main__.Laptop'>

 

그렇다면 type의 출력을 가지고 비교 연산을 하는 것과  isinstance가 보여주는 결과의 차이는 무엇일까요? 

 

# code
print(f"type(my_computer) == Computer: {type(my_computer) == Computer}")
print(f"type(my_computer) == Desktop : {type(my_computer) == Desktop}")
print(f"type(my_computer) == Laptop  : {type(my_computer) == Laptop}")

# type(my_computer) == Computer: False
# type(my_computer) == Desktop : False
# type(my_computer) == Laptop  : True
# code
print(f"isinstance(my_computer, Computer): {isinstance(my_computer, Computer)}")
print(f"isinstance(my_computer, Desktop) : {isinstance(my_computer, Desktop)}")
print(f"isinstance(my_computer, Laptop)  : {isinstance(my_computer, Laptop)}")

 

1) type의 출력으로 비교 연산을 했을 때의 결과는 인스턴스를 생성할 때 사용한 클래스에 대해서만 True를 반환한다는 것입니다.

2) isinstance의 경우 자식 클래스와 부모 클래스 모두에 대해서 True를 반환합니다.

 

아래의 링크는 type()과 isinstance()의 차이에 대해서 다루는 stackoverflow의 글입니다.

 

https://stackoverflow.com/questions/1549801/what-are-the-differences-between-type-and-isinstance

 

What are the differences between type() and isinstance()?

What are the differences between these two code snippets? Using type: import types if type(a) is types.DictType: do_something() if type(b) in types.StringTypes: do_something_else() Using

stackoverflow.com

 

3. type()으로 클래스 메서드 사용하기

    다음으로는 조금 특이해 보일 수도 있는 코드에 대해서 다뤄보겠습니다. type을 통해서 반환되는 것은 객체(인스턴스)의 타입 정보이기 때문에 이를 통해서 클래스 메서드를 활용할 수 있습니다. 이때 주의하실 점은 self에 해당하는 인스턴스를 전달해주어야 한다는 점입니다. 

 

# code
print("type(my_computer).get_laptop_info(my_computer):")
type(my_computer).get_laptop_info(my_computer)
print()

# type(my_computer).get_laptop_info(my_computer):
# name    : AMD Laptop
# cpu name: AMD Rygen 5
# gpu name: Radeon
# battery info: ABC Battery

 

만약 인스턴스를 전달해주지 않는다면 self가 전달되지 않았다고 에러가 발생합니다.

 

# code
print("type(my_computer).get_laptop_info():")
type(my_computer).get_laptop_info()
print()

# Exception has occurred: TypeError
# get_laptop_info() missing 1 required positional argument: 'self'

 

여기까지 읽다 보면 의문이 듭니다. 굳이 이렇게 까지 어렵게 코드를 만들 필요가 있을까요? 하지만 이를 활용하면 아래와 같이 코드를 짤 수 있습니다. 동일한 클래스로부터 만들어진 인스턴스라면 클래스 메서드를 type을 통해서 사용할 수 있습니다.

 

print("type(my_computer).get_laptop_info(your_computer):")
type(my_computer).get_laptop_info(your_computer)
print()

# type(my_computer).get_laptop_info(your_computer):
# name    : Intel Laptop
# cpu name: Intel i5
# gpu name: RTX 3060
# battery info: DEF Battery

 

    이 글을 마무리하고 있는 상황에서도 이게 최선이었을까? 단순히 책의 저자의 방식에 불과하기 않았을까? 하는 의문이 계속 듭니다. 그래서! 책에 나온 코드도 공유드립니다. 아래는 저자의 github 주소입니다. chromosome.py와 genetic_algorithm.py의 코드를 보시면 됩니다. 기회가 된다면 해당 코드들에 대해서도 블로그에서 다뤄보겠습니다.

 

https://github.com/davecom/ClassicComputerScienceProblemsInPython/tree/master/Chapter5

 

GitHub - davecom/ClassicComputerScienceProblemsInPython: Source Code for the Book Classic Computer Science Problems in Python

Source Code for the Book Classic Computer Science Problems in Python - GitHub - davecom/ClassicComputerScienceProblemsInPython: Source Code for the Book Classic Computer Science Problems in Python

github.com

 

긴 글 읽어주셔서 감사합니다. 
글과 관련된 의견은 언제든지 환영입니다.​
반응형

댓글