디버깅은 버그(오류)가 발생한 위치를 파악해서 분석한 후 올바르게 고치는 과정
파이썬 프로그램을 디버깅할 때 print 함수와 형식화 문자열을 사용하거나 logging 내장 모듈을 사용해 출력을 만들면 아주 긴 출력이 생긴다.
우리에게 필요한 작업은 프로그램이 실행되는 동안 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)
print('5')
int_value = 5
str_value = '5'
print(f'{int_value} == {str_value} ?')
5
5
5 == 5 ?
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함수에 넘길 수 없고, 객체의 인스턴스 필드에 대한 정보도 전혀 들어 있지 않다.
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, '뭐시기')
obj = OpaqueClass(4, 'baz')
print(obj.__dict__)
{'x': 4, 'y': 'baz'}