매직 메서드와 빌트인 함수의 차이
<매직 메서드>
프로그래밍 언어에서 사전에 정의해놓은 이름을 가지는 메서드
메서드라는 이름답게, 주로 클래스로부터 생성된 객체가 호출할 수 있는 함수이다.
<Python에서의 매직 메서드>
<Python에서의 매직 메서드 예시>
__init__():
생성자 함수.
인스턴스 초기화 할 때 호출된다. 즉, 객체 생성시 호출된다.
__call__():
인스턴스가 호출됐을 때 실행.
클래스 안에 이 함수를 쓰면 클래스로부터 생성된 객체를 호출가능하게 만들어준다.
__repr__():
호출하면 객체에 대해 설명하는 텍스트를 주로 리턴한다.
custom class의 경우 좋은 포맷을 구현해 놓았다면
디버깅할 때 수월하게할 수 있다.
__setattr__():
속성과 그 대응하는 값을 추가할 때 사용
__hash__():
빌트인 함수 hash()에 의해 호출되며 integer를 리턴한다.
같은 객체인지의 비교를 위해 사용한다.
사용하고자하는 class가 __eq__() 메서드를 구현하지 않았다면
__hash__() 또한 구현하지 않아야한다.
아래 예시 코드는 public, protected, private 속성, 메서드값을 알아보기 위해 작성한 코드인데
매직 메서드를 설명할 수도 있어서 같은 코드를 사용하였다.
출력값은 해당 객체에 어떤 프로퍼티와 메서드들이 구현되어있는지 리스트 형태로 보여준다.
dir() 빌트인 함수를 통해 출력하였다.
class Student:
__homeschool = "j_School" # private class attribute
__teacher = "Andrew Drake" # private class attribute
def __init__(self, name, age): # instance attribute
self.name = name # instance public attribute
self.__age = age # instance private attribute
# def __ display(self): # private method
# print("This is private method")
class ChildStudent(Student):
__another_school = "b_School"
__teacher = "MJ"
std = Student("Kim Seung Joo", 20)
print(std.name)
# print(std.age)
print(dir(std))
# std.__homeschool
# AttributeError: 'Student' object has no attribute '__homeschool'
cstd = ChildStudent("Kim Sung Joo", 29)
print(dir(cstd))
Kim Seung Joo
['_Student__age', '_Student__homeschool', '_Student__teacher', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
['_ChildStudent__another_school', '_ChildStudent__teacher', '_Student__age', '_Student__homeschool', '_Student__teacher', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
파이썬 공식문서의 데이터 모델을 읽다보면
겹치는 이름의 매직메서드-빌트인 메서드 쌍이 많다.
__hash__()는 hash() 빌트인 함수를 사용하며
__bool__()는 bool() 빌트인 함수를 사용한다.
__setattr__(key, value)는 빌트인 함수 setattr(instance, key, value)에 의해 호출되는 class method이다.
왜 굳이 매직 메서드의 형태로 사용할까 ?
객체에 attribute를 추가하는 방법은 여러가지이나
객체지향측면에서 __setattr__가 가장 적합하기 때문이다.
아래 예시를 보자
class Citizen:
def __init__(self):
self.age = 0
self.gender = 0
c=Citizen()
c.age=20 # 1
c.__setattr__('gender', 'Female') # 2
setattr(c,'age',30) # 3
3가지 방식 모두 c라는 이름의 객체의 attribute을 변경한다.
해당 속성이 존재하지 않으면 새 attribute을 부여한다.
# 1
특별한 경우를 제외하고는 # 1방법을 가장 많이 사용할 것이다.
c.age = 20은 내부적으로 매직 메서드 __setattr__를 호출한다.
# 2
# 2번 방법은 특별한 경우, 즉, __setattr__를 오버라이딩해서 기능을 변경하거나
__init__ 시점에서 속성:값 을 kwargs로 받아 초기화하는 경우가 아니면 쓸 일이 없다.
#3
파이썬 데이터 모델 설계상 파이썬 객체 속성 변경시
# 1과 같은 방법이 있으므로 굳이 빌트인 함수로 변경할 필요가 없으며
가독성이 떨어진다고 생각한다.
__dict__에 접근하여 추가할 수도 있으나,
모든 내부 변수들을 들고있는 객체의 딕셔너리에 접근하여 attribute를 추가하기보다
base model의 __setattr__()를 사용하는 것이 권장된다.
정리하자면
1. __dict__ 직접 접근을 통한 속성 부여 -> 객체지향적이지 않음
2. obj.{attribute} = value -> 내부적으로 __setattr__를 호출하고, setattr 빌트인 함수를 사용함
새 attribute를 부여할 때는 recursive call의 가능성 때문에 부적합하고
object.__setattr__(self, name, value)의 방법이 권장된다.
3. __setattr__를 직접 조작하는 경우는
객체 초기화시 kwargs나 args로 데이터를 받을 때 속성을 추가하는 경우가 있는데
위 use-case는 떠오르는 것이 아직 없다.
빌트인 함수와 같은 기능인데 왜 매직 메서드를 쓰냐라고 묻는다면
현재까지의 내 경험으로는 다음과 같이 대답할 것 같다.
"빌트인함수보다 매직메서드가 더 객체지향적이다"
"파이썬 객체지향 데이터 모델이 그렇게 설계되었으며 보일러 플레이트가 마련되어있다"
"a 객체의 속성은 a 객체의 메서드가 관장하는 것이 맞다고 생각한다."
추가로
"파이썬의 기본 class를 활용할 때 attribute 및 객체의 속성을 다루는 메서드들이
구현되어 있기 때문이다. 꼭 필요할 때 말고는 이 메서드들을 재정의할 필요가없으며
high-level에서는 알 필요가 없다. 이미 class를 선언하는 순간 매직메서드를 쓰는것과 같다."
답변들이 너무나도 당연한 것 같지만
"평소에도 같은 기능인데 왜 굳이?" 라는 질문의 답에는
객체지향 개념이 있었던 케이스가 많아서 정리해보았다.
참고자료
https://stackoverflow.com/questions/5755023/why-to-use-setattr-in-python
https://stackoverflow.com/questions/10707206/what-is-the-difference-between-set-and-setattr-in-python-and-when-should
댓글
댓글 쓰기