20230103 TIL | __repr__() 함수는 왜 쓸까요?

Choog Yul Lee·2023년 1월 3일
0

2023_TIL

목록 보기
2/6
post-thumbnail

📆 Date

  • 3rd January 2023

🔑 Problem

  • python으로 LinkedList 를 자료구조를 구현해보라고?

note

LinkedList 를 구현한 걸 인터넷에서 찾아 보니 __repr__(self) 함수가 보인다. 안의 내용은LinkedList 내부 노드를 출력하기 위해 한 것인데 처음 보는 놈이다. 아놔 저건 뭐하는 놈인고!


🛰️ Reference Site

🎽 Learn

클래스를 정의할 때 __init__ 함수를 정의하게 된다. 이렇게 함수명 앞뒤로 __ 가 붙어 있는 함수를 Dunder 또는 Magic 함수 라고 부른다.

1. Dunder 또는 Magic 함수 란?

  • 파이썬에서 Dunder 또는 Magic 함수란 함수명 앞뒤로 __ 언더스코어가 붙은 함수를 말한다.
  • 파이썬 내장 클래스인 int 함수를 살펴보면, 많은 Dunder 함수가 정의되어 있음을 확인할 수 있다.
>>> print(dir(int))

output:

['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__
', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__
pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshi
ft__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'i
mag', 'numerator', 'real', 'to_bytes']

__repr__ 함수를 처음 봤을 때는 javatoString 과 비슷한 줄 알았다. 하지만, 공식 문서를 찾아 보니 전혀 아니다.

2. repr(object) 함수란?

Return a string containing a printable representation of an object. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(); otherwise, the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a __repr__() method. If sys.displayhook() is not accessible, this function will raise RuntimeError.

"Return a string containing a printable representation of an object" 문장 때문에 __repr__()__str__() 처럼 재 정의 해서 사용하는 경우가 있는 것이 아닌지?

더 중요한 건!

eval(repr(object)) 를 했을 때 객체를 생성할 수 있어야 한다는 것! 이다.

예를 들어 설명하면,

  • repr() 함수를 사용하면 eval() 함수로 객체를 생성할 수 있다.
a = 'Difference between str() and repr()'
b = repr(a)
c = eval(b)

print(c)

output:

Difference between str() and repr()
  • str() 을 사용하면 eval() 함수로 객체를 사용할 수 없다.
a = 'Difference between str() and repr()'
b = str(a) # b에 str(a) 를 사용한 것에 집중!
c = eval(b)

output:

Traceback (most recent call last):
  File "/workspace/firstContainer/data_structure/Person.py", line 27, in <module>
    c = eval(b)
  File "<string>", line 1
    Difference between str() and repr()
                     ^
SyntaxError: invalid syntax

3. __repr()__ 함수를 재정의 해보자!

이제 __repr()__ 함수를 재정의 하여 eval() 를 이용하여 객체를 생성할 수 있도록 하자.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

클래스를 위와 같이 정리해 놓고 객체를 생성하여 print() 로 출력해 보자.
한 번은 그냥 출력하고, 또 한번은 repr() 함수를 사용하여 출력한다.

tom = Person('Tom', 20)
print(tom)
print(repr(tom))

output:

<__main__.Person object at 0x7f85cd1dd490>
<__main__.Person object at 0x7f85cd1dd490>

print(tom)print(repr(tom)) 모두 동일한 값을 출력 함을 알 수 있다.

이제 __str()__ 함수를 재정의 하고 객체를 print() 로 출력해 보자.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __del__(self):
        print('Bye! Bye! ' + self.name)
    
    def __str__(self):
        return 'My name is ' + self.name + '. I am ' + str(self.age) + '.'

tom = Person('Tom', 20)
print(tom)
print(repr(tom))

output:

My name is Tom. I am 20.
<__main__.Person object at 0x7f27e44a9590>

자, 이제 eval() 함수를 이용하여 객체를 생성할 수 있도록 __repr()__ 를 재정의하자.

어려울 것 없다.

그 객체에 해당하는 생성자 를 호출하는 구문을 넘겨주면 된다.
Tom 의 경우는 repr(tom) 을 했을 경우 Person('Tom', 20) 값이 반환되면 된다.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __del__(self):
        print('Bye! Bye! ' + self.name)
    
    def __str__(self):
        return 'My name is ' + self.name + '. I am ' + str(self.age) + '.'
    
    def __repr__(self):
        return 'Person(\''+ self.name +'\', ' + str(self.age) +')'

print(tom)
print(repr(tom))
print(eval(repr(tom)))

output:

My name is Tom. I am 20.
Person('Tom', 20)
My name is Tom. I am 20.

print(repr(tom)) 값이 생성자를 호출하는 문자열 임을 결과에서 볼수 있다.
그리고 eval() 함수를 통해 객체를 생성하고 print() 함수를 호출했을 때, __str__(self) 가 호출되어 결과가 나오는 것을 확인 할 수 있다.

💎 Thinking

  • class 를 정의할 때 print() 함수 대신 __str__(self) 를 정의해서 쓰자!
  • __repr__(self)eval() 을 사용하여 객체를 사용할 수 있도록 재정의 하자!
  • __str__(self) 가 정의되어 있지 않으면, default__repr__(self) 가 호출된다는 것을!

0개의 댓글