상수부터 멀티라인 람다까지··· '파이썬'에 추가됐으면 하는 기능 4가지 

InfoWorld
‘파이썬(Python)’에 어떤 기능이 추가되길 원하는가? 여기서는 파이썬에 추가됐으면 하는 기능 4가지를 살펴본다. 물론 가능성 있지만 단정하긴 어렵다. 

‘파이썬’은 끊임없이 발전하고 있는 언어다. 파이썬 소프트웨어 재단(Python Software Foundation)은 표준 라이브러리와 C파이썬(CPython) 참조 구현에 내용을 추가하는 것에 그치지 않고 언어 자체에 새 기능과 개선 사항들을 계속해서 도입하고 있다. 
 
ⓒ422737 (CC0)

예를 들면 파이썬 3.8(Python 3.8)의 새로운 기능인 대입 표현식은 ‘바다코끼리 연산자(walrus operator)’로도 부르며, 이는 특정 작업을 더욱 간결하게 만들어준다. 또 다른 새 기능인 패턴 매칭(pattern matching)은 가능한 한 많은 사례 중 하나를 평가하는 코드 작성을 수월하게 해 준다. 이 두 가지는 다른 언어에서 이미 유용하게 사용되고 있는 기능들에서 영감을 받은 것이다. 

이 두 가지 외에도 파이썬에 추가될 수 있는 유용한 기능들이 많다. 파이썬을 더 빠르고, 더 강력하며, 더 트렌디하게 만들 수 있도록 말이다. 또 어떤 것들이 있을까? 여기서는 파이썬에 추가된다면, 가치를 더할 수 있는 기능 4가지를 소개한다. 

상수(constants) 
파이썬에는 상수(constant) 개념이 따로 없다. 현재 파이썬의 ‘상수’는 작성 규칙(convention)의 문제다. 이를테면 전부 대문자와 밑줄로 이뤄진 이름(예: DO_NOT_RESTART)을 사용한다는 것은 해당 변수를 상수로 쓰겠다는 뜻이다. 

이와 비슷하게 typing.Final 유형 주석은 객체를 수정해서는 안 된다는 점을 린터(linter)에 알려주지만 런타임에 이를 적용하지는 않는다. 왜? ‘변경가능성(mutability)’이 파이썬의 기반이기 때문이다. 변수에 값을 할당하면(예: x=3 ) 로컬 네임스페이스에 이름(x)이 생성되고, 이는 정수 값 3인 객체를 가리키는 것을 의미한다. 

파이썬은 이름의 변경가능성을 항시 가정한다. 즉 어떤 이름이든 어떤 객체를 가리킬 수 있다. 즉 이름이 사용될 때마다 파이썬은 그 이름이 어떤 개체를 가리키는지 찾아보는 수고를 한다는 뜻이다. 이러한 역동성(dynamism)은 파이썬이 다른 언어에 비해 실행 속도가 느린 주요 이유 가운데 하나다. 파이썬의 역동성은 큰 유연성과 편리성을 제공하는 반면 런타임 성능의 희생이 뒤따른다.

파이썬에 상수 선언이 있다면 좋은 점 하나는 런타임 도중에 발생하는 객체 검색 빈도를 줄여 성능을 향상시킨다는 것이다. 만약 주어진 값이 절대 바뀌지 않는다는 사실을 런타임이 사전에 인지한다면 바인딩 검색을 할 필요가 없다. 이를테면 파이썬 앱(사이썬(Cython), 누티카(Nutika))에서 머신 네이티브 코드를 생성하는 시스템과 같은, 서드파티 최적화를 위한 추가적인 방안을 제공할 수 있다. 

그러나 ‘상수’는 중대한 변화일 것이고, 하위 호환이 안 되는 변화일 가능성이 크다. 또한 상수가 새로운 구문(예: 아직 사용된 적이 없는 $ 기호)으로 오게 될지 아니면 파이썬의 기존 이름 선언 방식의 확장 형태로 오게 될지도 논쟁거리가 될 것이다. 

마지막으로 더 큰 철학적 문제가 있다. 역동성이 매력 포인트인 언어에서 상수가 말이 되느냐 하는 문제다. 정리하자면 파이썬에서 상수를 보게 될 수도 있지만, 그렇다면 엄청난 변화가 될 것이다.

오버로딩과 제네릭(overloading and generics)
다양한 종류의 입력값과 함께 사용할 수 있도록 똑같은 함수를 여러 버전으로 작성할 수 있는 언어가 많다. 예를 들면 to_string() 함수에는 정수, 부동소수점 수, 다른 객체에서 변환하기 위한 서로 다른 구현체가 있을 수 있지만 편의상 똑같은 이름을 공유한다. ‘오버로딩’ 또는 ‘제네릭’은 소프트웨어 작성을 수월하게 해 준다. 주어진 유형에 맞는 메소드를 사용하는 대신 일반적인 프로세스에 맞춰 제네릭 메소드를 작성할 수 있기 때문이다. 

파이썬에서 하나의 함수 이름으로 많은 함수 작업을 할 수 있긴 하지만 함수의 여러 인스턴스를 정의하지는 않는다. 주어진 범위에서 이름은 한 번만 정의할 수 있고 한 번에 한 객체에만 바인딩할 수 있다. 따라서 동일한 이름을 가진 단일 함수의 여러 버전은 있을 수 없다.

이러한 문제를 해결하고자 파이썬 개발자들은 일반적으로 isinstance() 또는 type() 등을 사용해 함수에 제출된 변수의 유형을 알아낸 후, 그 유형에 따라 조치를 취하곤 한다. 때로는 내부의 유형별 함수 버전으로 디스패칭(dispatching)하는 경우도 있다. 그러나 이러한 접근방식은 해당 개발자가 함수를 확장할 수 있도록 만들지 않는 한 다른 개발자들의 함수 확장을 어렵게 만든다. 

2007년 4월 공개된 PEP 3124는 오버로드될 수 있음을 나타내고자 함수를 데코레이팅하는 메커니즘을 제안했다. 이는 거부되진 않았지만 유보됐다. 즉 기본적으로 좋은 아이디어지만 실행 시기가 맞지 않는다는 뜻이다. 

파이썬에서 오버로딩 채택을 가속화할 수 있는 한 가지 요인은 새로 제안된 패턴 매칭 시스템의 구현이다. 이론상 패턴 매칭은 내부적인 오버로드 디스패치 처리에 사용될 수 있지만 파이썬에 제네릭스를 구현하지 않을 근거로도 제시될 수 있다. 타입 시그니처를 기반으로 작업을 디스패치하는 방식을 이미 제공하고 있기 때문이다.

따라서 언젠가는 파이썬에 오버로딩이 생길지도 모른다. 아니면 오버로딩의 장점이 다른 매커니즘으로 대체될 수도 있다.

꼬리 재귀 최적화(Tail recursion optimizations)
많은 언어 컴파일러가 꼬리 재귀 최적화를 사용한다. 자기 자신을 호출하는 함수(재귀 함수)는 애플리케이션에서 새 스택 프레임을 생성하지 않으므로 무리하게 실행될 경우 스택 오버플로우가 일어날 수 있기 때문이다. 파이썬은 이를 지원하지 않으며, 사실 파이썬을 만든 사람들은 계속해서 이를 반대해 왔다.

그 이유 중 하나는 파이썬이 안팎으로 ‘재귀’ 대신 ‘반복’(생성자, 코루틴 등)을 사용하기 때문이다. 이 경우, 재귀 매커니즘 대신에 루프와 스택 구조가 있는 함수를 사용한다는 것을 의미한다. 루프의 각 호출은 스택에 저장돼 새로운 재귀를 만들 수 있으며, 재귀가 끝나면 스택 밖으로 나올 수 있다.

재귀 대신 이러한 패턴을 사용하도록 파이썬 개발자들에게 권장되고 있기 때문에 재귀 최적화에 대한 희망은 거의 없어 보인다. 그럴 확률이 분명하게 없다고 봐도 무방하다. 파이썬의 이디엄(idiom)이 다른 해결책들을 지원하기 때문이다.

멀티라인 람다(Multiline lambdas)
파이썬을 개발한 귀도 반 로섬의 반발이 있긴 했지만 파이썬은 현재 람다 또는 익명함수를 지원한다. 하지만 제약이 많다. 함수 본문으로 표현식 한 줄만(대입 연산에서 등호(=) 오른쪽에 있는 것) 사용할 수 있다는 것이다. 선언문의 전체 블록을 원한다면 이를 분리하고 이것으로 실제 함수를 만들어야 한다. 

그 이유는 반 로섬이 보는 파이썬의 설계에 있다. 반 로섬은 2006년 작성한 글을 통해 “들여쓰기 방식의 블록을 표현식 중간에 포함시킨 솔루션은 그 어떤 것이라도 용납할 수 없다. 선언문 그룹화를 위한 대체 구문(예: 중괄호, 시작/종료 키워드) 역시 마찬가지로 용납할 수 없기 때문에 멀티라인 람다는 풀 수 없는 퍼즐이 되는 셈이다”라고 말했다. 

다시 말해, 문제는 기술적인 것이 아니라 기존 파이썬 구문의 미학을 보완하는 멀티라인 람다용 구문이 없다는 점이다. 특별한 경우를 포함하지 않고는 이를 할 수 있는 방법은 없을 것이다. 또 ‘특별한 경우’가 많이 나타나는 언어는 사용하기 불편한 경향이 있다. 따라서 ‘유니콘(unicorn)’이 나타나기 전까지는 별도로 정의된 함수에 만족해야 할 것이다. 아마도 멀티라인 람다는 파이썬에 생기지 않을 가능성이 크다. ciokr@idg.co.kr