Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 448 – 추가 언패킹 일반화

Author:
Joshua Landau <joshua at landau.ws>
Discussions-To:
Python-Ideas list
Status:
Final
Type:
Standards Track
Created:
29-Jun-2013
Python-Version:
3.5
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 PEP는 * 이터러블 언패킹 연산자와 ** 딕셔너리 언패킹 연산자를 확장하여, 더 많은 위치에서 임의의 횟수로 언패킹하고 함수 호출 및 디스플레이에서 언패킹할 수 있도록 제안합니다.

함수 호출이 한 번만이 아니라 임의의 횟수로 언패킹을 지원하도록 제안합니다.:

>>> print(*[1], *[2], 3)
1 2 3
>>> dict(**{'x': 1}, y=2, **{'z': 3})
{'x': 1, 'y': 2, 'z': 3}

튜플, 리스트, 세트 및 딕셔너리 디스플레이 내부에서 언패킹을 허용하도록 제안합니다.:

>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}

딕셔너리에서는 나중에 나오는 값이 항상 먼저 나오는 값을 덮어씁니다.:

>>> {'x': 1, **{'x': 2}}
{'x': 2}

>>> {**{'x': 2}, 'x': 1}
{'x': 1}

이 PEP에는 리스트, 세트 및 딕셔너리 컴프리헨션 내부의 언패킹 연산자가 포함되지 않지만, 향후 제안에서 다루는 것이 배제된 것은 아닙니다.

근거

현재 * 이터러블 언패킹 연산자의 사용에는 가독성을 해칠 수 있는 불필요한 제한이 있습니다.

여러 번 언패킹하는 데에는 명백한 근거가 있습니다. 여러 이터러블을 함수 정의에 언패킹하거나 언패킹 뒤에 더 많은 위치 인자를 이어 쓰려는 경우, 가장 자연스러운 방법은 다음과 같이 작성하는 것입니다.:

function(**kw_arguments, **more_arguments)

function(*arguments, argument)

이것이 유용한 간단한 예로는 printstr.format이 있습니다. 대신 다음과 같이 작성하도록 강제될 수 있습니다.:

kwargs = dict(kw_arguments)
kwargs.update(more_arguments)
function(**kwargs)

args = list(arguments)
args.append(arg)
function(*args)

또는 그렇게 할 수 있다는 것을 알고 있다면 다음과 같이 작성할 수 있습니다.:

from collections import ChainMap
function(**ChainMap(more_arguments, arguments))

from itertools import chain
function(*chain(args, [arg]))

이러한 방법은 불필요한 줄 잡음을 추가하며, 첫 번째 방법에서는 작업이 중복됩니다.

컨테이너 내부에서 언패킹하는 데에는 두 가지 주요 근거가 있습니다. 첫째, 타입의 세부 사항을 무시하면 fst, *other, lst = elemselems = fst, *other, lst는 근사적인 역연산이 되는 할당의 대칭성이 있습니다. 이는 특수한 경우를 제거하여 결과적으로 언어를 단순화합니다.

둘째, 딕셔너리를 결합하는 것과 같은 “추가” 유형의 작업을 크게 단순화하며, 이를 모호하지 않고 명확하게 정의된 방식으로 수행합니다.:

combination = {**first_dictionary, "x": 1, "y": 2}

다음과 같은 방식 대신:

combination = first_dictionary.copy()
combination.update({"x": 1, "y": 2})

이는 표현식이 선호되는 문맥에서 특히 중요합니다. 또한 my_list + list(my_tuple) + list(my_range)와 같이 이터러블을 리스트로 합산하는 더 읽기 쉬운 방법으로도 유용하며, 이제 이는 단순히 [*my_list, *my_tuple, *my_range]와 동등합니다.

사양

함수 호출은 *** 언패킹을 제한 없이 여러 번 허용할 수 있습니다. 위치 인자와 * 언패킹의 순서에는 제한이 없으며, 키워드 인자와 ** 언패킹의 순서에도 제한이 없습니다.

함수 호출에는 키워드 인자가 위치 인자 뒤에 와야 하며 ** 언패킹은 추가로 * 언패킹 뒤에 와야 한다는 제한이 계속 적용됩니다.

현재 인자가 여러 번 주어지면 — 예를 들어 위치 인자가 위치로도 주어지고 키워드로도 주어지면 — TypeError가 발생합니다. 여러 ** 언패킹을 통해 제공되는 중복 인자에도 이 원칙이 적용됩니다. 예를 들어 f(**{'x': 2}, **{'x': 3})의 경우에도 마찬가지이지만, 오류는 실행 시간에 감지됩니다.

함수는 다음과 같은 형태입니다.:

function(
    argument or *args, argument or *args, ...,
    kwargument or *args, kwargument or *args, ...,
    kwargument or **kwargs, kwargument or **kwargs, ...
)

튜플, 리스트, 세트 및 딕셔너리에서는 언패킹을 허용합니다. 이는 함수 호출에서 언패킹할 때와 마찬가지로, 언패킹되는 항목의 요소가 언패킹 위치에 순서대로 삽입되는 것처럼 동작합니다. 딕셔너리에는 ** 언패킹이 필요하며, 나머지 모두에는 * 언패킹이 필요합니다.

딕셔너리의 키는 오른쪽에서 왼쪽으로 우선순위가 유지되므로 {**{'a': 1}, 'a': 2, **{'a': 3}}{'a': 3}으로 평가됩니다. 언패킹의 개수나 위치에는 제한이 없습니다.

단점

함수 호출에서 인자에 허용되는 순서는 이전보다 더 복잡합니다. 규칙을 가장 간단하게 설명하면 “위치 인자는 키워드 인자와 ** 언패킹보다 앞서고, * 언패킹은 ** 언패킹보다 앞섭니다”입니다.

*elements, = iterableelements를 리스트로 만들지만, elements = *iterable,elements를 튜플로 만듭니다. 그 이유는 이 구문에 익숙하지 않은 사람들을 혼란스럽게 할 수 있습니다.

딕셔너리에서는 중복 키가 허용되지만 함수 호출 구문에서는 중복 키로 인해 오류가 발생하는 예상 밖의 차이에 대한 우려가 제기되었습니다. 현재 구문에서도 이미 그러하지만, 이 제안으로 인해 문제가 악화될 수 있습니다. 실제로 이것이 어느 정도의 문제인지는 지켜봐야 합니다.

변형

PEP에서는 원래 함수 호출에서 인자 유형의 순서(위치 인자, 키워드 인자, * 또는 **)를 덜 엄격하게 만들 수 있는지를 고려했습니다. 이에 대한 지지는 거의 없었으므로 이 아이디어는 보류되었습니다.

이 PEP의 초기 버전에서는 리스트, 집합 및 딕셔너리 컴프리헨션 내부의 언패킹 연산자를 컨테이너 이터러블에 대한 평탄화 연산자로 허용했습니다:

>>> ranges = [range(i) for i in range(5)]
>>> [*item for item in ranges]
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]

>>> {*item for item in ranges}
{0, 1, 2, 3}

이에 대해서는 가독성에 대한 강한 우려와 미온적인 지지가 엇갈렸습니다. PEP에서 논란이 적은 부분이 불리해지지 않도록 이 내용은 제안의 나머지 부분과 함께 채택되지 않았습니다.

f(x for x in it)와 같은 함수 호출의 괄호 없는 컴프리헨션은 이미 유효합니다. 이는 다음과 같이 확장할 수 있습니다:

f(*x for x in it) == f((*x for x in it))
f(**x for x in it) == f({**x for x in it})

그러나 이것이 최선의 동작인지, 아니면 f를 호출할 때 인자로 언패킹해야 하는지는 명확하지 않았습니다. 이는 혼란을 일으킬 가능성이 높고 유용성도 매우 제한적이므로 이 PEP에는 포함하지 않습니다. 대신 이는 SyntaxError를 발생시키며, 명시적 괄호가 있는 컴프리헨션을 사용해야 합니다.

승인

이 PEP는 2015년 2월 25일 Guido에 의해 승인되었습니다 [1].

구현

Python 3.5용 구현은 버그 추적기의 Issue 2292에서 확인할 수 있습니다 [2]. 여기에는 현재 컴프리헨션 내부의 언패킹 지원이 포함되어 있으며, 이는 제거해야 합니다.

참고 자료

[3] Python-ideas 메일링 리스트 논의, “list / array comprehensions extension”, Alexander Heger (https://mail.python.org/pipermail/python-ideas/2011-December/013097.html)