알고리즘 문제를 파이썬으로 푸는 사람들은, 어떤 문제를 재귀함수를 구현해서 풀었을 때 RecursionError가 발생하는 것을 본 경우가 많을 것이다.
이때마다 setrecursionlimit()함수를 이용해서 재귀 깊이 제한을 풀어주면서 문제를 풀어갔었다.
하지만 나는 Java, C++로도 많은 문제를 풀었지만 이 재귀 깊이 문제는 항상 파이썬으로 풀 때에만 보이길래, 어떤 이유에서 이렇게 제한을 걸어뒀는지 궁금해서 찾아보게 되었다.
파이썬에서 재귀 깊이 제한은 기본적으로 1000이다.
이는
import sys
print(sys.getrecursionlimit())
를 실행해보면 알 수 있다.
생각해보면 깊이 제한 1000은 너무 적다고 생각했다.
왜 이렇게 낮게 잡혀있는지 찾아보았다.
https://docs.python.org/3/library/sys.html?utm_source=chatgpt.com 문서를 참고해보니
sys.getrecursionlimit()
Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python. It can be set by
setrecursionlimit().
라고 나와있고, 해석해보면 무한재귀에 의해 오버플로우 발생을 막는다고 한다.
다시 나의 궁금했던 점으로 돌아가보면, 나는 왜 하필 "파이썬"만 이렇게 제한이 되어있는가가 궁금했다.
잘 보면 C stack 이라고 하는데, 알다시피 파이썬은 기본적으로 CPython 구조체를 이용한다.
사실 C단에서 오류가 나면 예외 처리 등이 없이 그냥 프로그램이 종료가 된다는 것을
나는 pyside, pyqt, pytorch 등을 이용하면서 알게 되었다.
외부 라이브러리 없이 파이썬에서도 파이썬 내부 오류가 아닌 C단에서의 오류를 만들어낼 수 있는데, 먼저 가장 기본적인 예외처리 코드인 아래 코드를 보자.
try:
print(3/0)
except:
print("except 실행")
finally:
print("finally 실행")
0으로 나누면 예외(오류)가 발생하는건 유명한 예외 중에 하나다.
위 코드를 실행하면 당연히 예외가 발생하므로 except블록 내의 코드가 실행되고 그 다음에 finally 부분이 실행되므로
except 실행
finally 블록 실행
와 같이 출력되고 끝날 것이다.
import ctypes
try:
print("try 시작")
ctypes.string_at(0) # 0번 주소는 접근 금지된 영역
except:
print("except 실행")
finally:
print("finally 실행")
이걸 실행하면, print("try 시작")부분만 출력되고
그 후 except, finally는 전혀 실행되지 않고
Process finished with exit code 139 (interrupted by signal 11:SIGSEGV)와 같이 비정상 종료된다.
방금 했던 3/0 같은 0나누기로 예외처리를 다 한 경우에는 예외가 잘 처리되므로
exit code가 0이 되어야하지만, ctypes로 접근 금지 영역에 강제로 접근하면 내부가 C로 처리 되기 때문에 이와 같이 예외 처리는 물론이고 finally역시 동작하지 않고 그냥 종료 되어버린다.
즉 프로세스가 아예 종료된다.
Mac OS 기준으로

이와 같은 경고 메시지도 보인다.
이게 더 골치아픈게, Pycharm등의 IDE로 디버그 모드에서 예외 발생시 자동 break를 설정하더라도 아예 잡히지도 않고, 예외 내용이 출력되지 않는다.
이를 파이썬 재귀 함수 제한을 생각해보니, 만약에 제한없이 무한재귀를 이용하다가 C단에서 스택오버플로우가 발생해서 이렇게 프로그램이 아무 소리도 없이 종료된다면
굉장히 골치아플 것 같다.
GPT에 질문해도
파이썬은 재귀 호출마다 C 스택 프레임을 사용하므로, 재귀가 너무 깊어지면 시스템 스택이 넘쳐서 Segmentation Fault (세그멘테이션 오류) 등으로 파이썬 인터프리터 자체가 크래시할 수 있습니다.
라고 답변을 했다.
다른 이유를 찾아본 바로는, Python은 꼬리 재귀 최적화(Tail Call Optimization)를 지원하지 않기 때문에 재귀 함수 호출 대신에 이를 반복문 형태로 변환하는 것이 더 옳다고 한다.
꼬리 재귀 최적화에 대해서는 링크에 따로 정리해두었다.