PEP 3113 – 튜플 매개변수 언패킹 제거
- Author:
- Brett Cannon <brett at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 02-Mar-2007
- Python-Version:
- 3.0
- Post-History:
Table of Contents
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
튜플 매개변수 언패킹은 시퀀스 인자가 자동으로 언패킹되도록 함수 시그니처에서 튜플을 매개변수로 사용하는 것입니다. 예는 다음과 같습니다.:
def fxn(a, (b, c), d):
pass
시그니처에서 (b, c)를 사용하려면 함수의 두 번째 인자가 길이 2인 시퀀스(예: [42, -13])여야 합니다. 이러한 시퀀스가 전달되면 매개변수에 해당 값이 할당되면서 언패킹되며, 이는 매개변수에서 b, c = [42, -13] 문이 실행된 것과 같습니다.
안타깝게도 Python의 풍부한 함수 시그니처 기능에 포함된 이 기능은 일부 상황에서는 편리하지만 그 가치보다 더 많은 문제를 일으킵니다. 따라서 이 PEP는 Python 3.0에서 이 기능을 언어에서 제거할 것을 제안합니다.
제거해야 하는 이유
인트로스펙션 문제
Python은 매우 강력한 인트로스펙션 기능을 제공합니다. 이러한 기능은 함수 시그니처에도 적용됩니다. 함수의 호출 시그니처가 무엇인지에 관한 숨겨진 세부 정보는 없습니다. 일반적으로 함수 객체와 그 객체의 다양한 속성(함수의 func_code속성 포함)을 확인하면 함수 시그니처의 여러 세부 정보를 쉽게 파악할 수 있습니다.
그러나 튜플 매개변수와 관련해서는 큰 어려움이 있습니다. 튜플 매개변수의 존재는 함수 코드 객체의 co_varnames속성에서 해당 이름이 .와 숫자로 구성되어 있는 것으로 표시됩니다. 이를 통해 튜플 인자를 바이트코드만 인식할 수 있고 Python 소스에서는 입력할 수 없는 이름에 바인딩할 수 있습니다. 그러나 이것만으로는 튜플의 형식, 즉 길이, 중첩 튜플의 존재 여부 등을 지정할 수 없습니다.
함수에서 튜플에 관한 모든 세부 정보를 얻으려면 함수의 바이트코드를 분석해야 합니다. 이는 함수의 첫 번째 바이트코드가 튜플 인자를 언패킹하는 동작을 그대로 나타내기 때문입니다. 튜플 매개변수의 이름이 .1이고 spam 및 monty 변수로 언패킹될 것으로 예상된다고 가정하면(즉, 튜플이 (spam, monty)임), 함수의 첫 번째 바이트코드는 spam, monty = .1 문에 해당합니다. 따라서 튜플 매개변수의 모든 세부 정보를 알려면 함수의 초기 바이트코드를 살펴보고 \.\d+형식의 매개변수에 대한 튜플 언패킹을 감지한 다음 예상되는 인자에 관한 가능한 모든 정보를 추론해야 합니다. 바이트코드 분석을 통해 inspect.getargspec함수가 튜플 매개변수에 관한 정보를 제공할 수 있습니다. 이 작업은 쉽지 않으며 인트로스펙션 도구에 부담이 됩니다. 이러한 도구는 Python 바이트코드의 작동 방식을 알아야 하기 때문입니다(다른 모든 유형의 매개변수에는 Python 바이트코드에 관한 지식이 필요하지 않으므로 원래는 필요 없는 부담입니다).
바이트코드 분석의 어려움은 차치하더라도 Python 바이트코드 사용에 대한 의존성과 관련된 또 다른 문제가 있습니다. IronPython [3]은 Python의 바이트코드를 사용하지 않습니다. 관련 .NET 프레임워크를 기반으로 하므로, 대신 함수의 func_code.co_code 속성에 MSIL [4]을 저장합니다. 이 사실 때문에 IronPython에서 실행할 경우 inspect.getargspec함수가 작동하지 않습니다. 다른 Python 구현도 영향을 받는지는 알려지지 않았지만, 해당 구현이 Python 가상 머신을 단순히 재구현한 것이 아니라면 영향을 받을 것으로 보는 것이 합리적입니다.
제거해도 기능 손실 없음
Introspection Issues에서 언급했듯이, 튜플 매개변수를 처리하기 위해 함수의 바이트코드는 인자를 적절한 매개변수 이름으로 언패킹하는 데 필요한 바이트코드로 시작합니다. 이는 튜플 매개변수를 구현하는 데 특별한 지원이 필요하지 않다는 뜻이며, 따라서 튜플 매개변수를 제거해도 기능 손실은 없고 가능한 편의성만 사라진다는 뜻입니다. 이 편의성은 Why They Should (Supposedly) Stay에서 다룹니다.
이 PEP의 시작 부분에 있는 예제 함수는 쉽게 다음과 같이 재작성할 수 있습니다.:
def fxn(a, b_c, d):
b, c = b_c
pass
그러면서도 기능을 전혀 잃지 않습니다.
규칙에 대한 예외
Python 함수가 가질 수 있는 다양한 유형의 매개변수를 살펴보면, 튜플 매개변수는 규칙이라기보다 예외에 가까운 경향이 있음을 알 수 있습니다.
다음 두 PEP를 고려하십시오: PEP 3102 (키워드 전용 인자) 및 PEP 3107 (함수 어노테이션)입니다. 두 PEP 모두 승인되었으며 함수 시그니처 내에 새로운 기능을 도입합니다. 그러나 두 PEP 모두 새로운 기능을 튜플 매개변수 전체에 적용할 수 없습니다. PEP 3102는 튜플 매개변수를 전혀 지원하지 않습니다(튜플 매개변수를 이름으로 참조할 방법이 없으므로 이는 타당합니다). PEP 3107은 튜플 내 각 항목에 대한 어노테이션(예: (x:int, y:int))은 허용하지만, 튜플 전체에 대한 어노테이션(예: (x, y):int)은 허용하지 않습니다.
튜플 매개변수가 존재하면 함수 시그니처에서 시퀀스 객체와 매핑 객체도 서로 별도로 배치됩니다. 매핑 객체(예: 딕셔너리)를 매개변수로 전달하고, 시퀀스가 튜플 매개변수로 언패킹되는 것과 동일한 방식으로 언패킹되도록 할 방법은 없습니다.
불충분한 오류 메시지
다음 함수를 살펴보십시오.:
def fxn((a, b), (c, d)):
pass
fxn(1, (2, 3))으로 호출하면 TypeError: unpack non-sequence라는 오류 메시지가 표시됩니다. 이 오류 메시지만으로는 어느 튜플이 올바르게 언패킹되지 않았는지 전혀 알 수 없습니다. 또한 이것이 인자 때문에 발생한 결과라는 표시도 없습니다. 함수 인자와 관련된 다른 오류 메시지들은 시그니처와의 관계를 명시적으로 밝힙니다. 예를 들어 TypeError: fxn() takes exactly 2 arguments (0 given) 등이 있습니다.
적은 사용량
제가 개인적으로 알고 지내는 소수의 Python 프로그래머들과 PyCon 2007 스프린트에서 실시한 비공식 설문은 대다수가 이 기능을 알지 못하며 나머지도 단지 사용하지 않는다는 점을 보여주지만, 이 기능이 많이 사용되지 않는다는 주장을 뒷받침하려면 객관적인 수치가 필요합니다.
함수 및 메서드 정의를 찾기 위해 정규 표현식 ^\s*def\s*\w+\s*\(을 사용하여 Python 코드 저장소의 Lib/ 디렉터리에 있는 모든 줄을 반복한 결과, 트렁크에서 22,252개의 일치 항목이 발견되었습니다.
튜플 매개변수를 포함하는 def 문을 찾기 위해 .*,\s*\(를 덧붙였더니 41개의 일치 항목만 발견되었습니다. 이는 def 문 중 튜플 매개변수를 사용하는 것은 0.18%에 불과한 것으로 보인다는 의미입니다.
이들이 (아마도) 유지되어야 하는 이유
실용적 용도
특정 상황에서는 튜플 매개변수가 유용할 수 있습니다. 흔한 예로 데카르트 좌표를 나타내는 두 항목짜리 튜플을 예상하는 코드가 있습니다. x 좌표와 y 좌표를 대신 언패킹해 주도록 할 수 있다는 점이 좋은 것은 사실이지만, 이러한 소량의 실용적 유용성은 튜플 매개변수와 관련된 다른 문제들에 비해 크게 상쇄된다는 것이 이 주장의 요지입니다. 또한 No Loss of Abilities If Removed에서 보인 것처럼, 튜플 매개변수의 사용은 순전히 실용적인 것이며 다른 방식으로 매우 쉽게 처리할 수 없는 고유한 능력을 전혀 제공하지 않습니다.
매개변수의 자기 문서화
튜플 매개변수는 특정 시퀀스 형식일 것으로 예상되는 매개변수를 자기 문서화하는 방법을 제공한다는 주장이 제기되어 왔습니다. Practical Use의 데카르트 좌표 예제에서처럼 함수의 매개변수로 (x, y)를 보면, 해당 매개변수의 인자로 길이가 2인 튜플이 예상된다는 점이 분명해집니다.
그러나 Python은 매개변수의 용도를 문서화하는 몇 가지 다른 방법도 제공합니다. 문서 문자열은 어떤 인자가 예상되는지 설명하는 데 필요한 충분한 정보를 제공하기 위한 것입니다. 튜플 매개변수는 시퀀스 인자에 대해 예상되는 길이를 알려 줄 수 있지만, 해당 데이터가 어떤 용도로 사용될지는 알려 주지 않습니다. 모든 매개변수가 튜플 매개변수가 아니라면 어떤 다른 인자가 예상되는지 알려면 문서 문자열도 읽어야 합니다.
함수 어노테이션은 튜플 매개변수에서는 작동하지 않지만 문서를 제공할 수도 있습니다. 어노테이션은 어떤 형식이든 될 수 있으므로, 한때 튜플 매개변수였던 것은 tuple, tuple(2), Cartesian point, (x, y) 등의 어노테이션이 지정된 단일 인자 매개변수가 될 수 있습니다. 어노테이션은 특정 길이의 시퀀스인 경우를 포함하여, 매개변수에 대해 인자가 어떤 것이어야 하는지 문서화하는 데 큰 유연성을 제공합니다.
전환 계획
튜플 매개변수가 제거되는 Python 3.x로 Python 2.x 코드를 전환하려면 두 단계가 제안됩니다. 먼저 Python 2.6에서 Python 컴파일러가 튜플 매개변수를 만났을 때 적절한 경고를 발생시켜야 합니다. 이는 Python 2.6과 비교하여 Python 3.0에서 발생할 다른 구문 변경과 동일하게 처리됩니다.
둘째, 2to3 리팩터링 도구 [1] 에 튜플 매개변수를 함수의 첫 번째 문장에서 압축 해제되는 단일 매개변수로 변환하는 픽서 [2] 가 추가됩니다. 새 매개변수의 이름은 변경됩니다. 그런 다음 새 매개변수는 튜플 매개변수에서 원래 사용된 이름들로 압축 해제됩니다. 이는 다음 함수가:
def fxn((a, (b, c))):
pass
다음과 같이 번역된다는 의미입니다.:
def fxn(a_b_c):
(a, (b, c)) = a_b_c
pass
단일 표현식 제한 때문에 람다에서 튜플 매개변수가 사용되므로, 람다에서도 지원해야 합니다. 이는 예상되는 시퀀스 인자를 단일 매개변수에 바인딩한 다음 해당 매개변수를 인덱싱하여 수행합니다.:
lambda (x, y): x + y
다음과 같이 번역됩니다.:
lambda x_y: x_y[0] + x_y[1]
참고 자료
Copyright
This document has been placed in the public domain.