repr
문자열을 사용하라.repr
을 호출하면, repr
로 얻은 문자열을 eval
내장 함수에 전달하면, 원래 값을 돌려받을 수 있다.%s
: str과 마찬가지로 사람이 읽을 수 있는 문자열 생성str.%r
: repr
과 마찬가지로 출력 가능한 문자열을 만들어 낸다.!r
접미사를 붙이지 않고 텍스트 치환식을 사용하면, 사람이 읽을 수 있는 형태의 문자열이 만들어진다.__repr__
special method를 정의해서,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__())
>>>
# 전부 똑같은 결과
foo 뭐시기
repr
로 나타낸 버전이다.% 연산자에 %r 형식화 문자열을 사용하는 것
이나, f-문자열에 !r 타입 변환을 사용하는 것
과 같다.print('%r' % 5) # 5
print('%r' % '5') # '5'
int_value = 5
str_value = 5
print(f'{int_value!r} != {str_value!r}')
__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__
attribute를 통해 객체의 instance dictionary에 접근할 수 있다.obj = OpaqueClass(4, 'baz')
print(obj.__dict__)
>>>
{'x':4, 'y':bar}
breakpoint
내장 함수 호출을 추가하면,debugger prompt
는 완전한 python shell이기 때문에,pdb
shell 명령어를 사용하면,프로그램의 상태를 관찰하는 과정
과 프로그램을 진행시키는 과정
을 번갈아가며 수행할 수 있다.python -m pdb -c continue 프로그램 경로
)import pdb; pdb.pm()
)를 사용대화형 디버거
breakpoint()
== pdb.set_trace()
대화형 파이썬 셸
이 시작된다.# always_breakpoint.py
import math
def compute_rmse(observed, ideal):
total_err_2 = 0
count = 0
for got, wanted in zip(observed, ideal):
err_2 = (got - wanted) ** 2
breakpoint() # 여기서 디버거를 시작함
total_err_2 += err_2
count += 1
mean_err = total_err_2 / count
rmse = math.sqrt(mean_err)
return rmse
result = compute_rmse(
[1.8, 1.7, 3.2, 6],
[2, 1.5, 3, 5])
print(result)
$ python3 always_breakpoint.py
> always_breakpoint.py(12)compute_rmse()
-> total_err_2 += err_2
(Pdb) wanted
>>> 5
(Pdb) got
>>> 7
pdb 사용법
p <이름>
으로 지역 변수 이름을 입력하면, 변수에 저장된 값 출력locals
호출하면, 모든 지역 변수 목록 볼 수 있음help
내장 함수 호출 가능where
up
(u
)down
(d
)step
next
return
continue
breakpoint
호출이나 중단점까지 프로그램을 계속 실행한다.quit
breakpoint 위치를 어디에 둘지 모르겠으면?
import math
def compute_rmse(observed, ideal):
total_err_2 = 0
count = 0
for got, wanted in zip(observed, ideal):
err_2 = (got - wanted) ** 2
total_err_2 += err_2
count += 1
mean_err = total_err_2 / count
rmse = math.sqrt(mean_err)
return rmse
result = compute_rmse(
[1.8, 1.7, 3.2, 7j], # 잘못된 입력
[2, 1.5, 3, 5])
print(result)
$ python3 -m pdb -c continue postmortem_breakpoint.py
import pdb; pdb.pm()
)를 사용...
ZeroDivisionError: ~~~
>>> import pdb; pdb.pm()
> my_module.py(13) compute_variance()
-> variance = err_2_sum / (len(data) - 1)
(Pdb) err_2_sum
0.0
(Pdb) len(data)
1
gc
모듈은 어떤 객체가 존재하는지 이해할 때는 도움이 되지만,tracemalloc
내장 모듈은tracemalloc
# waste_memory.py
import os
class MyObject:
def __init__(self):
self.data = os.urandom(100)
def get_data():
values = []
for _ in range(100):
obj = MyObject()
values.append(obj)
return values
def run():
deep_values = []
for _ in range(100):
deep_values.append(get_data())
return deep_values
# top_n.py
import tracemalloc
tracemalloc.start(10) # 스택 깊이 설정
time1 = tracemalloc.take_snapshot() # 이전 스냅샷
import waste_memory
x = waste_memory.run() # Usage to debug
time2 = tracemalloc.take_snapshot() # 이후 스냅샷
stats = time2.compare_to(time1, 'lineno') # 두 스냅샷을 비교
for stat in stats[:3]: # 메모리를 낭비하는 가장 큰 3가지 이유 찾기
print(stat)
>>>
waste_memory.py:5: size=2392 KiB (+2392KiB), count=29994 (+29994), average= 82 B
waste_memory.py:10: ...
waste_memory.py:11: ...
tracemalloc
모듈은, 각 할당의 전체 stack trace를 출력한다.tracemalloc.start
함수가 전달한 프레임 최대 개수만큼만 거슬러 올라가며 출력한다.# with_trace.py
import tracemalloc
tracemalloc.start(10)
time1 = tracemalloc.take_snapshot()
import waste_memory
x = waste_memory.run()
time2 = tracemalloc.take_snapshot()
stats = time2.compare_to(time1, 'traceback')
top = stats[0]
print('가장 많이 사용하는 부분은:')
print('\n'.join(top.traceback.format()))