[Python] magic method란?

Hyeseong·2020년 12월 2일
0

python

목록 보기
3/22
post-thumbnail

파이썬 매직 메서드는?

파이썬 매직 메서드는 특수 메서드 또는 던더 메서드라고도합니다. __init__ ()와 같이 이중 밑줄로 둘러싸여 있습니다.

매직 메소드 사례를 통한 이해

아래 코드 스니팻은 Human이라는 클래스를 만들어 human(소문자)으로 인스턴스화하여 진행할 부분입니다.

class Human : 
    def __init__ (self, id, name, addresses = [], maps = {}) : 
        self.id = id 
        self.name = name 
        self.addresses = addresses 
        self.maps = maps

1. Class

만약 dir(human) method를 실행해보면 29개의 메소드와 속성들이 리스팅 되고 그중 26개가 magic method입니다

꽤 큰 비중을 차지하는 만큼 중요도도 높다고 봐요.

__delattr__

이 메서드는 클래스에서 속성을 삭제하려고 할 때 호출됩니다.
예를 들어, Human 객체 del first.name 에서 name 속성을 삭제하려면 __delattr__ () 메서드가 호출됩니다.

Human 클래스에서 메서드를 구현하여 기능을 재정의 할 수 있습니다.

def __delattr __ (self, item) : 
    print ( 'Deleting attribute'+ item) 
    return object .__ delattr __ (self, item)

output

del first.name 
print (first.name) 
# 속성 이름 삭제를 반환합니다.

print (first.name) 
AttributeError : 'Human'개체에 'name'속성이 없습니다.

속성에 값을 할당하는 __setattr__ () 메서드와 속성에서 값을 가져 오는 __getattr__ () 메서드도 있습니다.

__delattr__ ()의 사용 사례는 객체의 특정 속성이 삭제되는 것을 방지하거나 특정 속성이 삭제 될 때 특정 작업을 수행하려는 경우 일 수 있습니다.

__dict__

이 메서드는 개체를 나타내는 dictionay를 반환합니다. dictionay의 key는 객체의 속성이고 value는 해당 속성의 값입니다.

예를 들어 :

human = Human (2, 'Malik') .__ dict__ 
print (human)

output

{ 'id': 2, 'name': 'Malik', 'addresses': [], 'maps': {}}

__dir__

클래스에서 __dir__ () 메서드를 재정 의하여 dir () 메서드를 재정의 할 수 있습니다. 예를 들어 dir () 메서드가 반환 한 결과에서 내부 메서드를 제거 할 수 있습니다.

def __dir __ (self) : 
    return list (filter (lambda x : not x.startswith ( '_'), object .__ dir __ (self)))
    
print (dir (human)) 
# [ 'addresses', 'id', 'maps', 'name']을 반환합니다.

__eq__

이 메서드는 == 작업을 수행하려고 할 때 호출됩니다. 이름이 다르더라도 id 속성이 같을 때 두 human 객체가 같다고 생각해 봅시다.
이 기능을 달성하기 위해 __eq__ () 메서드를 재정의 할 수 있습니다.

first = Human (1, 'Farhad') 
second = Human (1, 'Malik') 

print (first == second)
# True

__format__

string.format ()을 시도 할 때마다 format () 메서드가 내부적으로 호출됩니다.

__ge__

≥ 비교를 시도 할 때마다 ge() 메서드가 호출됩니다. __ge__() 구현하기 위해 커스터마이징하여 사용해 볼 게요
예를 들어 두 개의 Human 객체가 있다고 가정합니다.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')

더 큰 Id를 가진 Human 객체가 다른 Human 객체보다 더 큰 것으로 간주된다는 규칙이 있다고 생각해 봅시다.
따라서 __gt__ () 메서드를 재정의하고 커스터마이징 로직을구현할 수 있습니다.

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

이제 두 번째 개체의 ID가 첫 번째 사람 개체보다 크기 때문에 False를 반환합니다.

print (first> = second) 
False 반환

참고로, 연산자 ≤이 수행 될 때 실행되는 lt() 메서드도 있습니다.

__hash__

해싱은 개체를 정수로 변환하는 데 사용됩니다. dictionary/set으로 항목을 설정하려고 할 때 해싱이 수행됩니다.

좋은 해싱 알고리즘은 더 적은 수의 해싱 충돌을 발생시킵니다. __hash__() 메서드를 재정의하여 자체 해시 알고리즘을 만들수 있어요.

Human 객체의 ID가 우리 애플리케이션에서 고유하다고 가정 해 봅시다. __hash__ () 알고리즘은 self.id를 해시 정수로 반환하도록 재정의 할 수 있습니다.

def __hash__(self):
    return self.id

두 개의 개체를 만들어 집합 컬렉션에 저장할 수 있습니다. 집합의 길이를 쿼리 할 때 두 개체의 ID가 서로 다르기 때문에 집합에 두 개의 요소가 있어야합니다.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set([first, second])
print(len(my_set))
#returns 2

이제 두 객체 모두에 대해 id를 1로 설정하고 연습을 반복하면 두 객체의 이름이 동일하더라도 동일한 해시 키를 가지기 때문에 집합에서 1 개의 요소 만 볼 수 있습니다. 속성이 다릅니다.

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set([first, second])
print(len(my_set))
#returns 1

__init__

__init__ () 메서드는 생성자를 호출하여 클래스의 새 인스턴스를 인스턴스화하려고 할 때 실행됩니다.

예를 들어 실행을 시도했을 때 :

human = Human(1, 'farhad')

__init__ () 메서드가 자동으로 실행됩니다.

사용자는 이 역시 아래와 같이 기능과 인자들을 커스터마이징 할 수 있어요.

예를 들어, Human 클래스의 __init__ () 메서드는 다음과 같습니다.

def __init__(self, id, name, addresses=[], maps={}):
        self.id = id
        self.name = name
        self.addresses = addresses
        self.maps = maps

__init_subclass__

이것은 메타 클래스 사용 사례 중 하나입니다. 클래스가 서브 클래화 되고 그 객체가 생성되면 __init_subclass__() 메서드가 호출됩니다.

기본적으로 메서드는 부모에게 하위 클래스가 지정되었음을 알립니다. 이러한 연결은 지정된 클래스의 모든 하위 클래스를 초기화 할 수 있습니다.
따라서이 메서드는 하위 클래스를 등록하고 하위 클래스의 속성에 기본값을 할당하는 데 사용됩니다. 따라서 하위 클래스 초기화를 사용자 정의로 지정 할 수 있습니다.

__new__

클래스의 새 인스턴스를 인스턴스화하고 생성하려면 __new__ (cls) 메서드가 실행됩니다.
__init__() 메서드는 __new__ () 메서드가 성공적으로 완료된 후에 실행됩니다.

__new__()는 클래스에 대한 정적 메서드입니다. 이는 메서드가 객체의 상태에 의존하지 않고 클래스 수준에 있음을 의미합니다.

예를 들어, Human() 생성자가 호출 될 때마다 'Human creating…'을 출력하고 싶다고 생각해 봅시다.

아래와 같이 __new__ (cls) 메서드의 기능을 재정의 할 수 있습니다.

def __new__(cls, *args, **kwargs):
    print('Human creating...')
    return object.__new__(cls)

결과적으로 Human creating …은 인스턴스를 만들려고 할 때 인쇄됩니다.

human = Human(1, 'Farhad', ['Address1', 'Address1'], {'London':2, 'UK':3})

__sizeof__

이 메서드는 sys.getsizeof ()를 실행할 때 호출됩니다. 메모리에있는 개체의 크기를 바이트 단위로 반환합니다.

__str__

__str__ () 메서드는 개체를 인쇄 가능한 형식으로 인쇄하려고 할 때 실행됩니다. str () 메서드의 기능을 재정의 할 수 있습니다.

예를 들어 :

class Human:
    def __init__(self, id, name, addresses=[], maps={}):
        self.id = id
        self.name = name
        self.addresses = addresses
        self.maps = maps
        
    def __str__(self):
        return f'id={self.id}. name={self.name}'
human = Human(1, 'Farhad', ['Address1', 'Address1'], {'London':2, 'UK':3})
print(human)

id=1. name=Farhad.를 출력하게 되요.
__str__ () 함수는 사용자에게 친숙한 객체 표현을 반환해야합니다.

__weakref__

이 __weakref__ 객체는 대상 객체에 대한 weak references 리스트를 반환합니다. 기본적으로 가비지 수집이 참조가 수집되었음을 weak reference에 알리는 데 도움이됩니다.
따라서 기본 포인터에 액세스하는 개체를 방지합니다.

2. Integer

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.id)))))

__add__

이 메서드는 두 개의 숫자를 더하려고 할 때 호출됩니다.
예를 들어 :

human.id + 2는 human.id.__add__ (2)와 동일합니다.
또한 *를 수행 할 때 __mul__이 호출되고, '-'연산자를 사용할 때 __sub__가 호출되고, / 연산자를 사용하려고하면 __div__가 실행됩니다.

__and__

이 메소드는 & 연산자를 사용하려고 할 때 실행됩니다. 예 :
self & another_value 반환

__bool__

이 메소드는 객체에 대해 부울 검사를 수행하려고 할 때 실행됩니다.
자기! = 123

__floordiv__

이 메서드는 // operator를 실행할 때 실행됩니다.

__getnewargs__
때때로 우리는 파이썬에서 객체를 피클합니다. 피클 링은 필요한 경우 디스크에 저장할 수있는 메모리에있는 개체의 바이트 스트림 표현을 만듭니다.

__getnewargs__() 메서드는 피클 링 프로세스에 피클 링 된 개체를 다시로드하여 대상 개체를 다시 만드는 방법을 알려줍니다. 특히 new () 메서드에 인수를 전달하여 객체를 만드는 방법입니다. 따라서 이름은 ' get new args'입니다.

__index__
이 메소드는 객체를 인덱스로 사용하려고 할 때 실행됩니다. 예를 들어 my_list [0]을 실행하려고하면 my_list [0 . index ()]와 동일합니다.

결과적으로 index () 메서드를 실행하여 객체를 정수로 변환 할 수 있습니다. 또한 메서드를 재정의하고 인덱스 생성 방법에 대한 자체 기능을 제공 할 수도 있습니다.

__invert__
이 메서드는 ~ 연산자를 사용할 때 실행됩니다.
예를 들어 :

first = Human(1, 'Farhad')
print(first.id.__invert__())

다음을 실행하는 것과 동일합니다.

first = Human (1, 'Farhad') 
print (first.id .__ invert __ ())

__lshift__

이 방법은 값의 왼쪽 이동을 제공합니다 (예 : self << value). lshift () 메서드를 재정 의하여 << 연산자를 오버로드 할 수 있습니다.
참고 : rshift ()는 >> 연산자를 수행 할 때 실행됩니다.

__mod__

이 메서드는 % 연산자를 사용할 때 호출됩니다.

__neg__

이 메서드는 부정 연산자를 사용할 때 실행됩니다.

first.id — second.id

__subclasshook__

이 메서드를 재정의하여 issubclass() 메서드를 사용자 지정할 수 있습니다. 기본적으로 클래스가 하위 클래스이면 True를 반환하고 그렇지 않으면 False를 반환합니다. 이 메서드는 기존 알고리즘을 사용할 수 있도록하는 NotImplemented도 반환합니다.
이 메서드는 issubclass () 메서드의 결과를 사용자 지정할 수 있습니다.

3. String

사람의 이름 속성이 문자열 유형임을 알 수 있습니다. 그런 다음 속성에 대해 dir (human.name)을 수행하고 이중 밑줄로 둘러싸인 메서드를 필터링하면 총 33 개의 매직 메서드가 있음을 알 수 있습니다.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.name)))))

__contains__

이 메소드는 주어진 캐릭터가 존재하는지 확인하려고 할 때 실행됩니다.

__len__

이 메서드는 문자열의 길이를 반환합니다. len () 메서드를 수행 할 때 실행됩니다.
문자열의 길이를 계산하기 위해 특정 문자 만 계산하려면 해당 기능을 제공하기 위해 __()__ 메서드를 재정의 할 수 있습니다.

__repr__

이 메서드는 객체의 개발자 친화적 인 표현을 만들고 싶을 때 실행됩니다.

클래스에 __str__ () 구현이 없으면 print(object) 때 __repr__이 실행됩니다.

def __repr__(self):
    return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'
print(Human(1, 'Farhad'))

output

id=1 (<class ‘int’>). name=Farhad (<class ‘str’>)

__repr__ () 메서드는 객체의 공식적인 표현을 생성하기위한 것이어야합니다.

__iadd__

때때로 할당과 함께 더하기 연산자를 사용합니다.

self.id + = 1

이것은 self.id = self.id + 1과 같습니다.
iadd() 메서드는 할당과 함께 add를 할 때 실행됩니다.

곱셈과 할당을 수행 할 때 __imul__ ()이 실행되고-=를 사용할 때 __isub__ ()가 실행되고 /=를 실행할 때 __idiv__() 메서드가 실행된다는 점에 유의하는 것이 중요합니다.

또한 **=가 수행되면 __ipow__ () 메서드가 실행됩니다.
__iand__ () 매직 메서드는 할당과 함께 비트 AND를 수행하기위한 것이며 __ior __ ()는 다음과 같이! 를 시도 할 때 호출됩니다. i! = j

4. List

human 객체의 address 속성은 list 타입입니다. 그런 다음 addresses 속성에서 dir (human.addresses)를 수행하고 이중 밑줄로 둘러싸인 메서드를 필터링하면 총 35 개의 매직 메서드가 있음을 알 수 있습니다.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.addresses)))))

__reduce__

객체가 피클되면 reduce () 메서드가 실행되어 피클이 객체를 다시 생성하는 방법을 이해하는 데 도움이되는 객체를 반환합니다.

__reduce_ex__

__reduce_ex__ () 메서드는 __reduce__ () 메서드보다 pickle에 의해 선호됩니다.
__reduce_ex__ () 메서드는 프로토콜 버전 인 정수 인수를 사용합니다. 피클 링에 대한 이전 버전과의 호환성을 제공하며 피클 된 바이트 스트림을 객체에 구성하는 데 사용됩니다.

__reversed__

__reversed__ () 메서드는 컬렉션을 역순으로 되돌리려 고 할 때 실행됩니다.
reversed (collection) 또는 collection.reverse ()가 호출되면 실행됩니다. 때로는 reversed () 메서드의 기능을 변경하기로 결정합니다.
__reversed__ () 메서드를 재정의하면 원하는 결과를 얻을 수 있습니다.

5. Dictionary

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.maps)))))

__delitem__

이 메소드는 항목을 삭제할 때 실행됩니다.

del dictionary[key]

__getitem__

이 메서드는 키에 대한 항목을 가져 오려고 할 때 실행됩니다.

item = dictionary[key]

__setitem__

이 메서드는 사전에 항목을 설정하려고 할 때 실행됩니다.

dictionary[key] = item

__iter__

이 메서드는 컬렉션의 반복자를 반환합니다. 반복자는 컬렉션을 반복하는 데 도움이됩니다.

__iter__ () 메서드를 재정 의하여 iterator()가 실행되는 방식을 재정의 할 수 있습니다.

마지막으로 __call__()에 대한 참고 사항
객체를 호출 가능하게 만들고 싶다면?
human() 함수로 만들고 싶다고 생각해 봅시다.

__call__ () 메서드를 사용하면 클래스를 함수로 취급 할 수 있습니다.

human = Human(1, 'Farhad')
human()

Human 클래스에서 __call__ () 매직 메서드 구현한다면 의도한 기능을 만들어 낼 수 있어요.

def __repr__(self):
    return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'
def __call__(self, *args, **kwargs):
    print('You attempted to call')
    print('Arguments: ', args)
    print('Keyword Arguments: ', kwargs)
    print(self)
    print('Call completed')

output

You attempted to call
Arguments: ()
Keyword Arguments: {}
id=1 (<class ‘int’>). name=Farhad (<class ‘str’>)
Call completed

__call__ () 메서드를 재정의함으로써 이제 데코레이터를 구현하여 객체를 함수로 반환하거나 실제 객체를 전달하여 함수를 인수로 받아들이는 라이브러리를 호출 할 수도 있습니다.

참고:medium.com

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글