2023-01-25 22:38:48

파이썬 코드를 보다 보면 클래스 정의 부분에 __init__(), __str__(), __add__() 와 같은 메소드를 보시곤 했을 것입니다. __init__()은 생성자로 객체를 생성할 때 처리할 내용을 작성하기 위한 함수로서, 많이 보셨을 것입니다. 

 

그런데 그 외에 다른 __로 시작하고 __로 끝나는 메소드들이 클래스 안에 들어가 있으면 왠지 모르게 어렵게 느껴집니다. 하지만, 그것들의 존재 이유를 알고 나면 오히려 굉장히 유용한 메소드들이라는 사실에 반가운 마음이 들 것입니다. 이 메소드들을 매직 메소드라고 부릅니다. 특별한 이름을 가지고 있고 마법과 같은 일을 수행하기 때문에 그러한 이름이 붙은 것 같습니다. 

 

오늘은 몇 가지 매직 메소드를 직접 구현해보면서 이들과 친해지는 시간을 가져보도록 하겠습니다. 

 

자주 쓰이는 매직 메소드

__str__() 

class Player:
    def __init__(self, name: str, goal: int):
        self.name = name
        self.goal = goal


p1 = Player('손흥민', 23)
print(p1)

 

 

위와 같이 Player 객체를 하나 생성한 후 print 해보면 이 데이터가 존재하는 주소만 나올뿐 데이터에 대한 내용은 볼 수 없습니다. 그런데 print 했을 때 내용이 나오면 좋겠죠? 이런 경우에 __str__() 메소드를 정의해주면 됩니다. 

 

class Player:
    def __init__(self, name: str, goal: int):
        self.name = name
        self.goal = goal

    def __str__(self):
        return f'Player(name={self.name}, goal={self.goal})'


p1 = Player('손흥민', 23)
print(p1)

 

 

이렇게 print 해줬을 때 데이터 내용이 나오는 것이 주소가 나오는 것보다 훨씬 정보성이 있기 때문에 __str__() 메소드를 많이 사용합니다. 

 

__lt__(), __le__(), __eq__(), __ne__(), __gt__(), __ge() 

위 메소드들은 두 객체를 비교할 때 사용됩니다. 

 

class Player:
    def __init__(self, name: str, goal: int):
        self.name = name
        self.goal = goal

    def __str__(self):
        return f'Player(name={self.name}, goal={self.goal})'

    def __lt__(self, other):
        return self.goal < other.goal

    def __ge__(self, other):
        return self.goal >= other.goal

    def __eq__(self, other):
        return self.goal == other.goal


p1 = Player('손흥민', 23)
p2 = Player('황희찬', 15)

print(p1 < p2)
print(p1 >= p2)
print(p1 == p2)

 

 

보시다시피 <, >=, == 연산자 등이 우리가 원하는 기능을 하도록 만들어줄 수 있습니다. 저는 각 객체의 골 수로 크기를 비교하도록 했습니다. __lt__()는 <, __le__()는 <=, __eq__()는 ==, __ne__()는 !=, __gt__()는 >, __ge__()는 >=와 매치됩니다. 

 

__add__(), __sub__(), __mul__(), __truediv__(), __mod__(), __pow__() 

+, -, *, /, % 연산자도 우리가 원하는 기능을 수행하도록 해줄 수 있습니다. 

 

class Player:
    def __init__(self, name: str, goal: int):
        self.name = name
        self.goal = goal

    def __str__(self):
        return f'Player(name={self.name}, goal={self.goal})'

    def __lt__(self, other):
        return self.goal < other.goal

    def __ge__(self, other):
        return self.goal >= other.goal

    def __eq__(self, other):
        return self.goal == other.goal

    def __add__(self, other):
        return self.goal + other.goal

    def __sub__(self, other):
        return self.goal - other.goal


p1 = Player('손흥민', 23)
p2 = Player('황희찬', 15)

print(p1 + p2)
print(p1 - p2)

 

__add__() 메소드를 활용하여 두 객체의 골 수를 + 연산자를 통해 더할 수 있게 해봤습니다. 또한 __sub__() 메소드를 활용하여 두 객체의 골 수를 - 연산자를 통해 뺄 수 있게 해봤습니다. 

 

 

__call__()

인스턴스가 함수처럼 호출될 때 호출되는 메소드입니다. 

 

class Player:
    def __init__(self, name: str, goal: int):
        self.name = name
        self.goal = goal

    def __str__(self):
        return f'Player(name={self.name}, goal={self.goal})'

    def __lt__(self, other):
        return self.goal < other.goal

    def __ge__(self, other):
        return self.goal >= other.goal

    def __eq__(self, other):
        return self.goal == other.goal

    def __add__(self, other):
        return self.goal + other.goal

    def __sub__(self, other):
        return self.goal - other.goal

    def __call__(self):
        print(f"{self.name} 인스턴스를 함수처럼 호출하셨습니다.")



p1 = Player('손흥민', 23)
p2 = Player('황희찬', 15)

p1()
p2()

 

 

위 코드를 보시면 신기하게 p1, p2 인스턴스에 ()를 붙여주니 __call__() 메소드가 호출되었습니다. 

 

 

정리하며

이제 파이썬 코드에서 __메소드__()를 만나도 두렵지 않으시겠죠? 뭔가 특별한 기능을 수행하는 매직 메소드일 뿐입니다. 이 관점만 가지고 코드를 보아도 훨씬 코드 파악이 쉬워지실 것입니다. 

 

참고자료

[1] https://docs.python.org/ko/3.7/reference/datamodel.html#special-method-names  

[2] https://www.tutorialsteacher.com/python/magic-methods-in-python