betterway75디버깅 출력에는 repr문자열 사용

디버깅 출력에는 repr 문자열을 사용하라¶

디버깅

  • 디버깅은 버그(오류)가 발생한 위치를 파악해서 분석한 후 올바르게 고치는 과정

  • 파이썬 프로그램을 디버깅할 때 print 함수와 형식화 문자열을 사용하거나 logging 내장 모듈을 사용해 출력을 만들면 아주 긴 출력이 생긴다.

  • 우리에게 필요한 작업은 프로그램이 실행되는 동안 print를 호출해 상태가 어떻게 바뀌었는지 알아내고 무엇이 잘못됐는지 이해하는 것이다.

print함수는 인자로 받은 대상을 사람이 읽을 수 있는 문자열로 표시한다.

  • 예를 들어 기본 문자열을 출력하면 주변에 따옴표를 표시하지 않고 내용을 출력
print('foo 뭐시기')

foo 뭐시기

여러 가지 다른 방법을 사용해도 동일하다.

my_value = 'foo 뭐시기'
print(str(my_value))
print('%s' % my_value)
print(f'{my_value}')
print(format(my_value))
print(my_value.__format__('s'))
print(my_value.__str__())

문제는 어떤 값을 사람이 읽을 수 있는 형식의 문자열로 바꿔도 이 값의 실제타입과 구체적인 구성을 명확히 알기 어렵다는 점이다.

  • 예를 들어 print의 기본 출력을 사용하면 5라는 수와 '5'라는 문자열 타입 구분이 어렵다.
print(5)
print('5')

int_value = 5
str_value = '5'
print(f'{int_value} == {str_value} ?')

5
5
5 == 5 ?

디버깅 과정에서 print를 사용하면 이런 타입의 차이가 문제가 된다.

  • 디버깅을 할 때 원하는 문자열은 거의 대부분 객체를 repr로 나타낸 버전이다.
  • repr 내장 함수는 객체의 출력가능한 표현을 반환하는데, 출력 가능한 표현은 반드시 객체를 가장 명확하게 이해할 수 있는 문자열 표현이어야 한다.
a = '\x07'
print(repr(a))

'\x07'

print(a)

repr이 돌려준 값을 eval 내장 함수에 넘기면 repr에 넘겼던 객체와 같은 객체가 생겨난다.

b = eval(repr(a))
print(b)
assert a == b


print를 사용해 디버깅할 때는 값을 출력하기 전에 repr을 호출해서 타입이 다른 경우에도 명확히 차이를 볼 수 있게 만들어야 한다.

print(repr(5))
print(repr('5'))

5
'5'
repr을 호출하는 것은 %연산자에 %r 형식화 문자열을 사용하는 것이나 f-문자열에 !r 타입 변환을 사용하는 것과 같다.

print('%r' % 5)
print('%r' % '5')

int_value = 5
str_value = '5'
print(f'{int_value!r} != {str_value!r}')

5
'5'
5 != '5'

예를들어 파이썬 클래스의 경우 사람이 읽을 수 있는 문자열 값은 repr 값과 같다. 이는 인스턴스를 print에 넘기면 원하는 출력이 나오므로 굳이 인스턴스에 대해 repr를 호출할 필요가 없다는 뜻이다. 안타깝지만 object를 상속한 하위 클래스의 repr기본구현은 그다지 쓸모가 없다.

  • 예를 들어 다음 코드는 간단한 클래스를 정의하고 그 클래스의 인스턴스를 출력한다.
class OpaqueClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

obj = OpaqueClass(1, 'foo')
print(obj)

<main.OpaqueClass object at 0x000001C5A76CAEE0>

이 출력은 eval함수에 넘길 수 없고, 객체의 인스턴스 필드에 대한 정보도 전혀 들어 있지 않다.

  • 문제를 해결할 두가지 방법
    - 클래스 소스 코드를 변경할 수 있다면 객체를 다시 만들어내는 파이썬 식을 포험하는 문자열을 돌려 주는 repr 특별 메서드를 직접 정의할 수 있다.
    - 다음은 앞에서 본 클래스에 대해 repr 특별 메서드를 정의한 코드다.
class BetterClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'BetterClass({self.x!r}, {self.y!r})'
    
obj = BetterClass(2, '뭐시기')
print(obj)

BetterClass(2, '뭐시기')

  • 클래스 정의를 마음대로 바꿀 수 없는 경우에는 dict 애트리뷰트를 통해 객체의 인스턴스 딕셔너리에 접근할 수 있다.
  • 다음 코드는 OpaqueClass 인스턴의 내용을 출력한다.
obj = OpaqueClass(4, 'baz')
print(obj.__dict__)

{'x': 4, 'y': 'baz'}

좋은 웹페이지 즐겨찾기