Python 함수 초기값 주의하기
메모리가 점점 늘어나는 memory leak은 함수 파라미터 초기값 설정으로부터 발생할 수도 있다
발단
프로세스를 실행하고 시간이 지나면 지날수록 점점 처리 시간이 늘어났다. 어딘가 메모리가 새고 있고 어떤 변수에 값이 쌓이고 있다고 판단했다.
원인
구간을 좁혀나가며 디버깅해 찾았다. 정답은 함수 파라미터 초기값
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def arr_test(num: int, arr: list = []):
arr.append(num)
print(f"now arr is {arr}")
for i in range(10):
arr_test(i)
# output
now arr is [0]
now arr is [0, 1]
now arr is [0, 1, 2]
now arr is [0, 1, 2, 3]
now arr is [0, 1, 2, 3, 4]
now arr is [0, 1, 2, 3, 4, 5]
now arr is [0, 1, 2, 3, 4, 5, 6]
now arr is [0, 1, 2, 3, 4, 5, 6, 7]
now arr is [0, 1, 2, 3, 4, 5, 6, 7, 8]
now arr is [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
스택 메모리에 저장되길 기대했던 지역 변수 arr이 전역 변수가 되버린걸까?
그렇지는 않았다. method arr_test의 지역변수로 잘 들어오고 있다.
원인은 두 가지, python에서 함수가 초기값을 저장하는 방식과 python에서 함수는 객체라는 점이었다.
함수가 호출될 때마다 파라미터로 받은 객체를 생성하는게 아니라 default 파라미터들을 __defaults__에 저장한다. 그렇기 때문에 함수를 선언하면 함수 객체의 magic member variable로 default value를 저장하게 되는 것이다. (그래서 함수 실행하기 전에도 미리 그 값을 print해 볼 수 있다.)
(지역변수라고 믿었던 mutable list arr를 반복 전/후로 찍어보니 값이 변한 것을 알 수 있었다. ) 이렇듯 mutable 객체들을 default value로 지정할 때 주의해야한다.
+ 참으로 아찔하다. 내가 작성한 코드에 있지 않아서 다행이었다. pydantic에서 DTO를 지정할 때에도 위와 비슷한 일이 있었던 것 같다.
+참조 https://kukuta.tistory.com/321
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.