본문 바로가기

Python

Python 스페셜 메서드 len, getitem, iter, str, repr, new

728x90

스페셜 메서드(Special Method)란?

  • 스페셜 메서드란, 더블 언더스코어(__)로 시작하고 끝나는 메서드
  • Dunder Method(double underscore method)라고도 불린다.
  • 특별한 기능을 수행하며, Python 인터프리터에 의해 특정한 시점 또는 상황에서 자동으로 호출된다.
  • 클래스의 특정 동작을 커스터마이즈할 수 있게 해준다.

이제 대표적인 스페셜 메서드와 예제 코드를 설명한다.


__len__

__len__() 메서드는 객체 속성에 대해 그 길이를 반환하는 스페셜 메서드다.

class Sentence:
	def __init__(self, sentence):
    	self.sentence = sentence
    def __len__(self):
    	return len(sentence)
        
s = Sentence("In the beginning God created the heavens and the earth.")
print(len(s)) # print(Sentence.__len__(s))

>> 55

 


__getitem__

__getitem__() 메서드는 객체에 대해 특정 인덱스에 있는 데이터를 반환하는 메서드다.

class Sentence:
	def __init__(self, sentence):
    	self.sentence = sentence
    def __len__(self):
    	return len(sentence)
    def __getitem__(self, idx):
    	return self.sentence[idx]
        
s = Sentence("In the beginning God created the heavens and the earth.")
print(s[0]) # print(Sentence.__getitem__(s, 0))

>> I

 


__iter__

__iter__() 메서드는 반복자(iterator)를 반환하는 스페셜 메서드다.

class Sentence:
	def __init__(self, sentence):
    	self.sentence = sentence
    def __len__(self):
    	return len(sentence)
    def __getitem__(self, idx):
    	return self.sentence[idx]
    def __iter__(self):
    	return iter(self.sentence)
        
s = Sentence("In the beginning God created the heavens and the earth.")
for word in s:
	print(word)

>> I
>> n
>> 
>> t
>> h
>> e
>> 
>> b

 


__str__

__str__() 메서드는 객체를 문자열화하는 스페셜 메서드다.

class MyBlog:
	def __init__(self, blogname, url, category):
    	self.blogname = blogname
        self.url = url
        self.category = category
    def __str__(self):
    	return f"Blog name : {self.blogname}, URL : {self. url}, Category : {self.category}"
        
bithub = MyBlog("bithub", "bithub.tistory.com", "IT")
print(bithub) # print(str(bithub)) -> print(MyBlog.__str__(bithub))

>> Blog name : bithub, URL : bithub.tistory.com, Category : IT

 

* 참고 : print() 함수는 내부적으로 출력 대상에 str() 함수를 적용한다.


__repr__

__repr__() 메서드 역시 객체를 문자열화하는 스페셜 메서드다.

__str__와 __repr__의 차이점

  • __str__ : 라이브러리를 사용자들이 디버깅 목적으로 사용되며, 객체의 비공식적인 정보를 출력한다.
  • __repr__ : 라이브러리를 개발자들이 디버깅 및 개발 목적으로 사용되며, 객체의 공식적인 정보를 출력한다.
print(str(123))
print(repr(123))

print(str(3.14))
print(repr(3.14))

print(str("hello"))
print(repr("hello"))



>> 123
>> 123
>> 3.14
>> 3.14
>> hello
>> 'hello'

 

위 코드만 봤을 때는 __str__과 __repr__의 큰 차이가 없어 보인다.

하지만 아래 코드를 보자.

import datetime

now = datetime.datetime.now()
print(str(now))
print(repr(now))

>> 2024-03-28 16:13:15.000683
>> datetime.datetime(2024, 3, 28, 16, 13, 15, 683)

 

str(now)는 간결한 날짜시간 형태로 출력되는 반면,

repr(now)는 객체 생성 코드가 그대로 담겨 출력된다.

그래서 eval() 함수 사용 시 str() 함수 대신 repr() 함수를 사용한다.

 

import datetime

now = datetime.datetime.now()
print(eval(str(now))) # error
print(eval(repr(now))) # 2024-02-28 16:21:10.913033

 

* eval() 함수는 문자열로 된 명령어를 문자로 받아서 해당 명령어를 실행하는 함수다.

 

예시)

eval("print("hello")")
>> hello

 

따라서 __repr__ 함수를 구현할 때 반환값이 반드시 eval() 함수의 인자로 적합한 값이여야함을 명심하고 구현해야 한다.

 

* __repr__ 구현 예제

class MyBlog:
    def __init__(self, blogname, url, category):
        self.blogname = blogname
        self.url = url
        self.category = category
    def __str__(self):
        return f"Blog name : {self.blogname}, URL : {self. url}, Category : {self.category}"
    def __repr__(self):
        return f"MyBlog('{self.blogname}', '{self.url}', '{self.category}')"
        
bithub = MyBlog("bithub", "bithub.tistory.com", "IT")
bithub_clone = eval(repr(bithub))

print(bithub)
print(bithub_clone)

>> Blog name : bithub, URL : bithub.tistory.com, Category : IT
>> Blog name : bithub, URL : bithub.tistory.com, Category : IT

 


__new__

  • __new__() 메서드는 객체가 생성될 때 호출되는 스페셜 메서드다.
  • 기본적으로 클래스 메서드로 선언된다. 이유는 객체가 생성될 때 호출되므로 객체 없이 호출될 수 있도록 하기 위함이다.
  • 클래스 메서드이기 때문에 cls를 반드시 인자로 가져야 한다.
  • @classmethod 데커레이터를 따로 명시해줄 필요 없다.
  • 반드시 객체를 반환해야 한다. 객체를 반환하지 않으면 객체가 생성된 후 호출되는 __init__()이 호출되지 않는다.

* __init__() 메서드와 차이점

  • __init__()은 객체가 생성된 후 호출된다.
  • __new__()는 객체가 생성되기 전 호출되고 객체를 생성하여 반환한다.
class MyBlog:
    def __new__(cls):
        print("MyBlog.__new__")
        return object.__new__(cls)

    def __init__(self):
        print("MyBlog.__init__")
        pass
        
bithub = MyBlog()

>> MyBlog.__new__
>> MyBlog.__init__

 

* 유틸리티 클래스의 경우 클래스를 반환하도록 하여 객체가 생성되지 않도록 할 수 있다.

class Calculator:
    def __new__(cls):
        return cls
        
    @classmethod
    def add(cls, a, b):
        return a + b
        
    @classmethod
    def subtract(cls, a, b):
    	return a - b


print(Calculator.add(2,2))
calc = Calculator()
print(calc, calc.add(2, 2))

>> 4
>> <class '__main__.Calculator'> 4

 

calc 변수에 객체가 아닌 Class가 담긴 것을 확인할 수 있다.