배경
파이썬 코드를 보다보면 특히 클래스 구조에서 __init__ 과 같이 더블 언더헤드가 앞뒤로 붙어있는 메서드들이 있다.
다른 메서드들과 동일한 메서드인건가? init 말고도 유사한 형태의 메서드들이 있던데, 얘네는 뭐라고 부르지?
분명 파이썬 기초 공부할 때 배웠을텐데, 아마 제대로 집중을 안했나보다.
그래서 찾아보니 던더 메서드 혹은 매직 메서드라고 부른다는걸 알게 되었다.
던더 메서드/매직 메서드
매직메서드라고도 불리는 이 메서드는 이름 그대로 특별한 기능을 가진 메서드이다.
사용자가 정의한 클래스의 동작을 변경하거나 확장할 때 사용된다. 즉, 커스터마이즈할 수 있게 해주는 요소라고 보면 될 것 같다.
오버라이딩 개념이 생각났다. 객체지향 프로그래밍의 특징이라고 보여진다.
Double undersocres 에서 던더 라는 이름이 나온거 같다. 발음이 마음에 들어서 앞으로 그냥 던더 메서드라고 하겠다.
https://rszalski.github.io/magicmethods/
파이썬 공식 문서는 아닌거 같은데 그에 버금가는 퀄리티로 누군가가 정리해놓았다.
기본 3총사
__new__(cls, [...)
객체 인스턴스를 호출했을 때 첫번째로 불러오는 메서드 ( __init__ 보다 먼저 )
객체의 생성과 초기화에 사용되며 인스턴스를 반환한다.
또한, 생성된 인스턴스를 __init__ 으로 전달하는 역할을 한다.
class MyClass:
def __new__(cls):
print("__new__ 메서드 호출")
instance = super(MyClass, cls).__new__(cls)
return instance
def __init__(self):
print("__init__ 메서드 호출")
obj = MyClass() # __new__와 __init__가 호출됨
'''출력
__new__ 메서드 호출
__init__ 메서드 호출
'''
근데 생각해보면 클래스에서 __new__를 선언한 경우는 많이 보지 못했다.
실제로 대부분의 경우 __new__를 직접 정의하지 않고, __init__ 만 정의하여 객체의 초기화를 수행한다고 한다.
그렇다면, __new__ 메서드를 직접 정의하는 경우는 왜 그런지 궁금해졌다.
찾아보니 3가지의 경우가 있는데, 조금 딥해서.. 접어두겠다 궁금하면 펼처 보기
불변객체의 생성
불변객체는 생성된 후에 내부 상태를 변경할 수 없는 객체(ex 정수,문자열)
불변객체를 생성하려면 __new__메서드를 활용해 초기상태를 설정하고 이후에는 그 상태를 변경하지 않도록 해야함
아래 클래스는 2차원 평면상의 점을 나타내며, 한 번 생성된 후 좌표가 변경될 수 없는 불변 객체로 구현되어 있음
class ImmutablePoint:
def __new__(cls, x, y):
instance = super().__new__(cls)
instance._x = x
instance._y = y
return instance
def __str__(self):
return f"Point({self._x}, {self._y})"
# 불변 객체 생성
point = ImmutablePoint(3, 5)
print(point) # 출력: Point(3, 5)
# 좌표 변경 시도
point._x = 10
print(point) # 출력: Point(3, 5)
이러한 기능을 통해 데이터의 불변성을 유지할 수 있다.
서브클래스에서의 커스텀 생성
때로는 부모 클래스의 생성 과정을 수정하거나 확장해야 할 때가 있다.
이 때 __new__ 메서드를 서브클래스에서 오버라이딩하여 부모 클래스의 생성 로직을 수정할 수 있다
class Parent:
def __new__(cls):
print("Parent의 __new__ 호출")
instance = super().__new__(cls)
return instance
def __init__(self):
print("Parent의 __init__ 호출")
class Child(Parent):
def __new__(cls):
print("Child의 __new__ 호출")
instance = super().__new__(cls)
return instance
def __init__(self):
print("Child의 __init__ 호출")
child = Child()
'''결과
Child의 __new__ 호출
Parent의 __new__ 호출
Child의 __init__ 호출
'''
이를 통해 기존 클래스의 동작을 유지하면서, 자식 클래스의 특수한 요구사항을 반영한 객체 생성 가능
싱글톤 패턴
하나의 클래스 인스턴스만을 생성하고, 이후에는 항상 같은 인스턴스를 반환하는 디자인 패턴
__new__ 메서드 내에서 이미 생성된 인스턴스가 있을 경우 그 인스턴스를 반환하고, 없을 경우에만 새로운 인스턴스를 생성하여 반환한다.
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2) # True
이를 통해 항상 동일한 인스턴스를 반환하도록 할 수 있다.
__init__(self, [...)
클래스의 속성을 초기화하는 역할
일반적으로 __init__ 메서드에 전달되는 매개변수들은 객체를 생성할 때 사용자가 제공하는 값들.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Person 클래스의 인스턴스 생성
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
print(person1.name, person1.age) # 출력: Alice 30
print(person2.name, person2.age) # 출력: Bob 25
__init__ 역시 클래스에서 필수적으로 정의해야할 필요는 없지만, 객체 초기화 작업을 위한 용도로 많이 사용된다.
__del__(self)
생성자(constructor)의 역할을 하던 __new__ , __init__ 과 반대인 소멸자(desctructor) 역할
객체가 더 이상 필요하지 않을 때 메모리에 해제되기 전에 수행할 작업을 정의하는 데 사용한다.
파일을 열었을 때, 해당 파일을 닫는 작업이나, 네트워크 연결을 종료하는 작업 등을 수행한다.
그러나, __del__ 메서드 내에서 실행되는 코드가 예상대로 동작하는 것을 보장하기 어려울 수 있기 때문에, 이러한 작업은 __del__ 메서드보다는 다른 방식으로 처리하는 것이 더 안전하다.
또한, 파이썬에서는 객체가 더 이상 참조되지 않을 때(즉, 더 이상 사용되지 않을 때) 가비지 컬렉터가 해당 객체를 메모리에서 해제하여 자동으로 소멸시키기 때문에 __del__ 메서드를 명시적으로 구현해야 하는 경우는 드물다.
class MyClass:
def __init__(self, name):
self.name = name
def __del__(self):
print(f"Object {self.name} is being destroyed")
# 여기에 소멸 관련 작업을 수행할 수 있음
# 객체 생성
obj1 = MyClass("Object1")
obj2 = MyClass("Object2")
# 객체 참조 해제
obj1 = None
obj2 = None
# 가비지 컬렉터가 동작할 때 __del__ 메서드 호출
'''출력
Object Object1 is being destroyed
Object Object2 is being destroyed
'''
'🏷️CS > Python' 카테고리의 다른 글
가상환경 접근 간소화 (0) | 2023.12.20 |
---|---|
파이썬의 부동 소수점 연산 (0) | 2023.10.12 |
dir, help, inspect (0) | 2023.08.24 |
f-string 응용하기 (0) | 2023.08.05 |
[for 와 else 를 같이 사용] (0) | 2023.07.31 |