파이썬 매직 메소드

SeHun.J·2024년 5월 7일

매직 메소드

파이썬에는 매직 메소드라고 불리는 메소드가 있는데, 클래스를 구현하기 위해서 필요한 다양한 메소드가 있습니다. 예를 들어, __init__(self)는 클래스를 할당할 때 간섭할 수 있는 메소드입니다. 또한, 구현한 클래스에 연산자 처리도 이 매직메소드를 통해 구현할 수 있습니다. 이는 오버로딩을 활용하는 것과 같습니다.

특징

매직 메소드의 특징으로는 __메소드__와 같이 더블언더스코어로 묶인 형태입니다. 매직 메소드의 가장 큰 장점은 내가 정의한 클래스를 파이썬 내부에 정의된 클래스처럼 사용할 수 있게 됩니다. 파이썬에서 1 + 2 같은 연산을 할 수 있는 이유는 int 클래스에 __add__ 라는 매직 메소드가 정의되어 있기 때문인데, 직접 만든 클래스에서도 __add__ 매직 메소드를 정의하여 같은 동작을 구현할 수 있게 되는 겁니다.

Construction and Initialization (생성 및 초기화)

  • __new__(cls, ...) : 새로운 인스턴스를 만들때 가장 먼저 호출. 클래스를 받아 실제로 인스턴스를 생성하고 __init__에게 인자들을 넘겨준다.
  • __init__(self, ...) : 클래스의 initializer(생성자)로, 인스턴스가 생성되고 나서 호출된다.
  • __del__(self) : 객체가 제거될 때(garbage collected) 호출된다.

Controlling Attribute Access (속성 접근)

  • __getattribute__(self, name) : self.name으로 객체의 속성을 참조할 때 호출된다. 멤버변수 또한 예외없이 해당 매직메소드가 실행되며, return을 하지 않으면 None으로 대입된다. 그렇다고 getattr(object, name) 형태로 기존 속성값을 가져오려고 하면, 무한재귀호출에 빠지게 된다.
  • __getattr__(self, name) : 객체에 존재하지 않는 속성을 참조할 경우 호출된다.
  • __setattr__(self, name, value) : 속성의 존재 여부와 상관없이 self.name = value로 객체의 속성에 값을 할당할 때 호출된다.
  • __delattr__(self, name) : 속성의 존재 여부와 상관없이 del self.name으로 객체의 속성을 삭제할 때 호출된다.

Representing your Classes (클래스 표현)

  • __str__(self) : 인스턴스를 str()로 호출할 때 호출되며, 사용자에게 읽기 쉬운 문자열을 출력하는 것을 목표로 한다. (human-readable)
  • __repr__(self) : 인스턴스를 repr()로 호출할 때 호출되며, 기계가 읽기 쉬운 문자열을 출력하는 것을 목표로 한다. (machine-readable)
  • __unicode__(self) : 인스턴스를 unicode()로 호출할 때 호출되며, 유니코드 문자열을 반환할 때 사용한다.
  • __format__(self, formatstr) : 인스턴스를 format()으로 호출할 때 호출되며, 인스턴스를 새로운 문자열 포맷으로 출력하고 싶을 때 사용한다.
  • __hash__(self) : 인스턴스를 hash()로 호출할 때 호출되며, 정수(해시값)를 반환한다. 이 해시값과 __eq__를 사용하여 비교 연산을 수행할 수 있다.
  • __dir__(self) : 인스턴스를 dir()로 호출할 때 호출되며, 인스턴스가 가진 모든 속성들의 목록을 반환한다.
  • __nonzero__(self) : 인스턴스를 bool()로 호출할 때 호출되며, 인스턴스를 True 또는 False로 간주할지에 따라 boolean을 반환해야 한다.
  • __sizeof__(self) : 인스턴스를 sys.getsizeof()로 호출할 때 호출되며, 인스턴스의 사이즈를 byte단위로 반환한다.
  • __doc__ : 인스턴스의 docstring을 출력할 때 사용한다.
  • __dict__ : 인스턴스의 모든 속성과 값을 저장하는 딕셔너리

Container (컨테이너)

파이썬 container 객체에는 sequence 객체(list, str, tuple)와 dict 객체가 있다. 이때, in 연산을 수행할 수 있는 객체를 container라고 한다. 이러한 container 객체에 필요한 매직 메소드는 다음과 같다.

읽어보면 어떤 역할을 하는지 알 수 있기 때문에 자세한 설명은 생략합니다.

  • __len__(self)
  • __getitem__(self, key)
  • __setitem__(self, key, value)
  • __delitem__(self, key)
  • __iter__(self)
  • __reversed__(self)
  • __contains__(self, item)
  • __missing__(self, key)

Context Manager (with)

컨텍스트 매니저는 with 키워드를 사용하여 객체를 생성할 때 setup과 cleanup을 담당한다.

  • __enter__(self) : with 키워드로 생성된 블럭이 시작(setup)될 때 컨텍스트 매니저가 해야할 일을 정의한다. with 키워드의 target이나 as 뒤의 이름을 반환해야 한다.
  • __exit__(self, exception_type, exception_value, traceback) : 생성된 블럭이 종료(cleanup)될 때 컨텍스트 매니저가 해야할 일을 정의한다.

Operator (연산자)

  • __cmp__(self, other) : 가장 기본이 되는 비교 매직 메소드. 모든 비교 연산자 (<, ==, != 등)에 대해 실제로 동작을 구현해야 한다. self < other는 음수, self == other는 0, self > other는 양수를 반환해야 한다.
  • __eq__(self, other) : 항등 연산자, ==에 대한 동작을 정의한다.
  • __ne__(self, other) : 부등호 연산자, !=에 대한 동작을 정의한다.
  • __lt__(self, other) : 보다 작음 연산자, <에 대한 동작을 정의한다.
  • __gt__(self, other) : 보다 큼 연산자, >에 대한 동작을 정의한다.
  • __le__(self, other) : 보다 작거나 같음 연산자, <=에 대한 동작을 정의한다.
  • __ge__(self, other) : 보다 크거나 같음 연산자, >=에 대한 동작을 정의한다.

Number (숫자)

  • __pos__(self) : 단항 긍정 (ex. +obj)에 대한 동작을 정의한다.
  • __neg__(self) : 단항 부정 (ex. -obj)에 대한 동작을 정의한다.
  • __abs__(self) : 내장된 abs() 함수의 동작을 정의한다.
  • __invert__(self) : ~ 연산자를 사용하여 반전에 대한 동작을 정의한다.
  • __round__(self, n) : 내장된 round() 함수의 동작을 정의한다.
  • __floor__(self) : math.floor()에 대한 동작을 구현한다.

General Arithmetic Operators (일반 산술 연산자)

  • __add__(self, other) : 덧셈
  • __sub__(self, other) : 뺄셈
  • __mul__(self, other) : 곱셈
  • __floordiv__(self, other) : // 정수 나눗셈
  • __div__(self, other) : / 나눗셈
  • __truediv__(self, other) : 정확한 나눗셈을 정의한다, __future__ import division이 유효한 경우에만 동작한다.
  • __mod__(self, other) : % 나머지
  • __divmod__(self, other) : divmod() 내장 함수를 사용하여 long 나눗셈을 위한 동작 정의.
  • __pow__(self, power, modulo=None) : ** 지수
  • __lshift__(self, other) : << 왼쪽 비트 시프트
  • __rshift__(self, other) : >> 오른쪽 비트 시프트
  • __and__(self, other) : & 비트간 논리곱
  • __or__(self, other) : | 비트간 논리합
  • __xor__(self, other) : ^ 비트간 배타적 논리합

Reflected arithmetic operators (뒤집힌 산술 연산자)

뒤집힌 산술 연산자는 일반 산술 연산자에서 역순을 처리합니다.

obj + other (__add__)

위의 경우는 일반적인 덧셈입니다. 하지만, 그 순서가 바뀐 경우는 달라집니다.

other + obj (__radd__)

이와 같이 뒤집힌 산술 연산자는 일반적인 산술 연산자r(reflected)을 붙입니다.
ex. __radd__, __rsub__, __rmul__ ...

Augmented assignment (증가된 할당)

증가된 할당(Augmented assignment)을 위한 매직 메소드도 있습니다. 증가된 할당이 무엇인지 모르겠다면, 이런 케이스를 의미합니다.

x = 0
x += 1 # x = x + 1

하지만, 이런 케이스는 다른 표현으로 x = x+1과 같고, __add__ 매직메소드를 정의했다면, 실제로도 오류없이 작동합니다. 하지만 문제점은 x에 할당되는 타입입니다.

class CustomInt:
	def __init__(self, val):
    	self.val = val
    
    def __add__(self, other):
    	return self.val + other
 
x = CustomInt(2024)
x += 1
# 결과는 어떻게 될까요?

print(type(x))
# <class 'int'>

위와 같이 증가된 할당과 관련된 매직 메소드를 사용하지 않은 경우, __add__ 메소드가 있어서 연산에 오류가 발생하지는 않지만, CustomInt 클래스 대신 int 클래스가 대입됩니다.

class CustomInt:
	def __init__(self, val):
    	self.val = val
    
    def __add__(self, other):
    	return self.val + other
    
    def __iadd__(self, other):
        self.val += other
        return self
 
x = CustomInt(2024)
x += 1
# 결과는 어떻게 될까요?

print(type(x))
# <class '__main__.CustomInt'>

증가된 할당 연산자는 일반적인 산술 연산자i(increase)을 붙입니다.
ex. __iadd__, __isub__, __imul__ ...

그 외

그 외에도 정말정말 많습니다. 다 옮겨적고자 했는데, 너무 많네요.

  • 타입 변환 : __int__(self), __long__(self), __float__(self), __complex__(self), __oct__(self), __hex__(self), __index__(self), __trunc__(self), __coerce__(self, other)
  • 디스크립터 객체 : __get__(self, instance, owner), __set__(self, instance, value), __delete__(self, instance)

출처

profile
취직 준비중인 개발자

0개의 댓글