str 과 repr

파이썬 내장함수에는 str과 repr이라는 어찌보면 매우 비슷한 기능을 해 주는 함수가 있다.
str과 repr은 모두 객체를 문자열로 리턴해 주는 함수이다. 하지만 두개의 함수에는 약간의 차이가 있다.

str과 repr의 차이점

어떤 차이가 있는지 예제를 보며 알아보자. 먼저 숫자에 적용해 보자.

>>> a = 123
>>> str(a)
'123'
>>> repr(a)
'123'

숫자는 아무런 차이가 없어 보인다. 이번에는 문자열에 적용해 보자.

>>> a = "Life is too short"
>>> str(a)
'Life is too short'
>>> repr(a)
"'Life is too short'"

문자열은 str과 repr이 다른 결과값을 리턴해 주었다. str은 문자열 그대로를 리턴해 주었는데 repr은 단일인용부호(')가 좌우로 감싸여진 형태의 문자열을 리턴해 주었다.

음 왜 그럴까?

한가지 예를 더 들어보자.

>>> a = datetime.datetime(2017, 9, 27)
>>> str(a)
'2017-09-27 00:00:00'
>>> repr(a)
'datetime.datetime(2017, 9, 27, 0, 0)'

datetime으로 만들어진 객체는 매우 다른 결과를 리턴해 주었다.

str과 repr에는 다음과 같은 차이점들이 있다.

구분 str repr
성격 비공식적인 문자열을 출력 공식적인 문자열을 출력
사용목적 사용자가 보기 쉽게 하기 위해 문자열로 객체를 다시 생성할 수 있기 위해
누구를 위해 프로그램 사용자(end user) 프로그램 개발자(developer)

repr의 사용목적을 보면 "문자열로 객체를 다시 생성할 수 있기 위해" 라고 되어 있다. 문자열로 객체를 생성하기 위해서는 eval함수를 사용한다. 즉, 다음과 같이 datetime객체의 repr로 생성된 문자열에 다시 eval을 수행하면 datetime객체가 만들어 져야 한다는 말이다.

>>> a = datetime.datetime(2017, 9, 27)
>>> b = repr(a)
>>> eval(b)
datetime.datetime(2017, 9, 27, 0, 0)

위 예제에서 알아본 문자열도 마찬가지이다.

>>> a = "Life is too short"
>>> b = repr(a)
>>> eval(b)
'Life is too short'

하지만 str으로 리턴된 문자열을 eval로 수행했을 때는 다음과 같은 오류가 발생한다.

>>> a = "Life is too short"
>>> b = str(a)
>>> eval(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    Life is too short
                    ^
SyntaxError: unexpected EOF while parsing

문자열을 repr했을때 왜 단일 인용부호(')가 덧붙여서 나왔는지 이제 이해가 될 것이다.

클래스의 __str__ 과 __repr__

이번에는 사용자가 만든 클래스에서 repr과 str이 어떻게 적용되는지 확인해 보자.

class MyRepr:
    pass


obj = MyRepr()

print(repr(obj))
print(str(obj))

MyRepr이라는 아무런 내용도 없는 클래스를 작성한 후 obj라는 객체를 생성하였다. 그리고 repr과 str로 해당 객체를 출력해 보았다. 결과는 다음과 같이 출력된다.

<__main__.MyRepr object at 0x100656cc0>
<__main__.MyRepr object at 0x100656cc0>

repr이나 str으로 객체 출력시 디폴트 문자열이 출력되는 것을 확인 할 수 있다. 이번에는 repr 호출시 의미있는 문자열이 출력되도록 다음과 같이 수정해 보자.

class MyRepr:
    def __repr__(self):
        return "Hello MyRepr"


obj = MyRepr()

print(repr(obj))
print(str(obj))

클래스에 __repr__ 메서드를 구현하면 repr메서드 호출시 수행되게 된다. 따라서 repr로 obj호출시 다음과 같은 문자열이 출력될 것이다.

Hello MyRepr

마찬가지로 클래스에 __str__ 메서드를 구현하면 str메서드 호출시 수행되게 된다. 하지만 위 예제에서는 __str__ 메서드를 구현하지 않았는데도 str(obj) 출력시 다음과 같은 문자열이 출력되었다.

Hello MyRepr

이렇게 되는 이유는 str메서드 호출시 제일먼저 __str__ 메서드가 구현되어 있는지 확인하고 없으면 __repr__ 메서드를 호출하게 되기 때문이다.

이번에는 반대로 __repr__ 대신 __str__ 메서드만 구현해 보자.

class MyRepr:
    def __str__(self):
        return "Hello MyRepr"


obj = MyRepr()

print(repr(obj))
print(str(obj))

수행결과는 다음과 같이 출력된다.

<__main__.MyRepr object at 0x100656d68>
Hello MyRepr

repr은 호출시 __repr__ 메서드가 없으면 __str__메서드가 호출되지 않고 디폴트 값이 출력된다는 점을 알 수 있다. 즉 str은 __str__이 없을 경우 __repr__ 을 참조하지만 repr은 오직 __repr__만 참조한다는 사실을 알 수 있다.

이번에는 repr 메서드의 사용목적인 "문자열로 다시 객체를 생성"을 만족하기 위해서 다음과 같이 코드를 수정해 보자. (참고. repr의 출력 문자열을 eval을 이용하여 다시 객체로 만들 수 있어야 한다는 것은 필수 조건은 아니다. 다만 권고사항 정도라고 보면 된다)

class MyRepr:
    def __repr__(self):
        return "MyRepr()"

    def __str__(self):
        return "Hello MyRepr"


obj = MyRepr()

obj_repr = repr(obj)
new_obj = eval(obj_repr)
print(type(new_obj))

repr 호출시 "MyRepr()"을 리턴하여 eval수행시 MyRepr()이 수행되어 새로운 MyRepr 클래스의 객체가 생성되는 것을 확인할 수 있다.

eval로 생성된 new_obj의 type을 출력한 결과는 다음과 같다.

<class '__main__.MyRepr'>

박응용 860

2020년 7월 8일 9:16 오후

목록으로