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

Python 개선 제안 한국어 번역

PEP 640 – 사용하지 않는 변수 문법

Author:
Thomas Wouters <thomas at python.org>
Status:
Rejected
Type:
Standards Track
Created:
04-Oct-2020
Python-Version:
3.10
Post-History:
19-Oct-2020
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

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

거부 기록

운영 위원회(Steering Council)에서 거부했습니다: https://mail.python.org/archives/list/python-dev@python.org/message/SQC2FTLFV5A7DV7RCEAR2I2IKJKGK7W3/

초록

이 PEP는 사용하지 않는 변수를 위한 새로운 문법을 제안합니다. 이 문법은 값을 할당할 수는 있지만 그 밖의 용도로는 사용할 수 없는 의사 이름(pseudo-name)을 제공합니다. 할당은 실제로 일어나지 않으며, 대신 값은 버려집니다.

동기

Python에서는 실제로 결과가 필요하지 않은데도 대입을 해야 하는 경우가 꽤 흔합니다. 관례적으로 사람들은 이를 위해 "_"나 "unused" 같은 이름(또는 "unused"를 접두사로 붙인 이름)을 사용합니다. 이는 언패킹 할당에서 가장 흔합니다:

x, unused, z = range(3)
x, *unused, z = range(10)

for 루프와 컴프리헨션에서도 사용됩니다:

for unused in range(10): ...
[ SpamObject() for unused in range(10) ]

이런 경우에는 "_"를 사용하는 것이 아마 가장 흔하겠지만, 이는 국제화에서 "_"를 사용하는 방식과 충돌할 가능성이 있습니다. 국제화에서는 gettext.gettext() 같은 호출을 "_"에 바인딩하여 번역할 문자열을 표시하는 데 사용합니다.

Python에 패턴 매칭을 추가하자는 제안(원래는 PEP 622였으나 지금은 PEP 634, PEP 635, PEP 636으로 나뉘었습니다)에서 "_"는 추가적인 특별한 의미를 가집니다. 이는 와일드카드 패턴으로, 변수에 할당할 수 있는 자리에서 사용되어 무엇이든 매치되어야 하지만 어디에도 할당되지 않아야 함을 나타냅니다. 그곳에서 "_"를 선택한 것은 다른 언어의 "_" 사용 방식과 일치하지만, Python의 다른 곳에서 쓰이는 "_"와의 의미 차이는 상당합니다.

이 PEP는 할당에서 유효한 이름 대신 특별한 토큰인 "?"를 사용할 수 있도록 허용할 것을 제안합니다. 이는 "_"의 이점 대부분을 누리면서도, 그 외에는 일반 변수인 "_"의 다른 용도에 영향을 주지 않습니다. 동일한 와일드카드 패턴을 사용할 수 있도록 허용하면 패턴 매칭과 언패킹 할당이 서로 더 일관성을 갖게 됩니다.

근거

특정 변수를 사용하지 않는 것으로 표시하는 것은 코드의 목적을 명확하게 하는 데 도움이 되므로 유용한 도구입니다. 이는 특정 변수가 의도적으로 사용되지 않는다는 것을 코드를 읽는 사람뿐 아니라 자동화된 린터에게도 명확하게 보여 줍니다.

하지만 관례에도 불구하고 "_"는 특별한 변수가 아닙니다. 값은 여전히 할당되고, 그 값이 참조하는 객체는 스코프가 끝날 때까지 여전히 살아 있으며, 여전히 사용할 수도 있습니다. 또한 사용하지 않는 변수에 "_"를 쓰는 것이 완전히 보편적이지도 않습니다. 관례적인 국제화와 충돌하고, 그것이 일반 변수라는 점이 분명하지 않으며, "unused"라는 이름의 변수만큼 사용되지 않는다는 점이 명확하지도 않기 때문입니다.

패턴 매칭 제안에서는 와일드카드 패턴에 쓰이는 "_"가 별도의 스코프에 있다는 점 덕분에, 사용하지 않는 변수로서의 "_"가 가진 문제를 비켜 갑니다. 국제화와의 유일한 충돌은 혼동의 가능성뿐이며, "_"라는 전역 변수의 사용과 실제로 상호 작용하지는 않습니다. 하지만 이 와일드카드 패턴 용도로 "_"를 특별 취급하는 것은 여전히 문제가 됩니다: 패턴 매칭 안과 밖에서 "_"의 의미 그리고 뜻이 서로 다르다는 것은 Python의 일관성이 깨진다는 것을 의미합니다.

패턴 매칭 안팎 모두에서 사용하지 않는 변수를 위한 특별한 문법으로 "?"를 도입하면 그 일관성을 유지할 수 있습니다. 이는 국제화와의 충돌, 또는 _를 변수로 사용하는 다른 모든 경우와의 충돌을 피합니다. 이는 언패킹 할당을 패턴 매칭과 더 밀접하게 맞추어, 패턴 매칭을 언패킹 할당의 확장으로 설명하기 쉽게 만들어 줍니다.

코드 가독성 측면에서, 특별한 토큰을 사용하면 그 의미를 찾아보기가 더 쉬워지고("what does question mark in Python do" 대 "why is my _ variable not getting assigned to"), 값을 사용하지 않는 것이 실제 의도라는 점이 더 명확해집니다 – 그 값을 사용하는 것이 아예 불가능하기 때문입니다.

명세

새 토큰 "?", 즉 token.QMARK가 도입됩니다.

대입 문맥(현재 문법의 star_atom과 t_atom)에서 "?"를 허용하도록 문법을 수정하며, 이때 식별자가 NULL로 설정된 Name AST 노드가 만들어집니다.

Name 표현식의 식별자를 선택 사항으로 둘 수 있도록 AST를 수정합니다(현재는 필수입니다). 식별자가 비어 있는 것은 STORE 문맥에서만 허용됩니다.

CPython에서는 바이트코드 컴파일러를 수정하여, 식별자가 없는 Name 노드에 대해 STORE_NAME 대신 POP_TOP을 내보내도록 합니다. Name 노드의 다른 사용처도 식별자가 비어 있는 경우를 알맞게 처리하도록 수정됩니다.

수정된 문법 노드는 적어도 다음과 같은 형태의 대입에 사용됩니다:

? = ...
x, ?, z = ...
x, *?, z = ...
for ? in range(3): ...  # including comprehension forms
for x, ?, z in matrix: ...  # including comprehension forms
with open(f) as ?: ...
with func() as (x, ?, z): ...

언패킹 문맥이 아닌 곳에서 "?" 하나만 단독으로 사용하는 것은 일반 대입과 with 문에서 허용됩니다. 이렇게 단독으로 쓰는 것은 그 자체로 별 의미가 없으며, 이러한 특정 경우를 금지할 수도 있습니다. 하지만 for ? in range(3)은 분명히 쓸모가 있으므로, 적어도 일관성을 위해서라도 다른 경우에도 "?" 하나만 단독으로 사용하는 것을 허용하는 편이 더 합리적으로 보입니다.

"?"는 대입에만 사용할 수 있으므로, 복합 대입(? *= 2)에서 "?"를 사용하는 것은 허용되지 않습니다. 이름에 대입할 때와 마찬가지로 "?"가 여러 번 나타나도 유효하며, 각 대입은 서로 간섭하지 않습니다.

하위 호환성

새 토큰을 도입하는 것이므로 하위 호환성 문제는 없습니다. 유효한 문법 중 의미가 바뀌는 것은 없습니다.

"?"는 식별자로 간주되지 않으므로 str.isidentifier()의 동작은 바뀌지 않습니다.

다만 이제 Name 토큰의 식별자가 비어 있을 수 있으므로, AST는 호환되지 않는 방식으로 바뀝니다. AST를 사용하는 코드는 그에 맞게 수정해야 합니다.

교육 방법

"?"는 언패킹 대입과 함께 소개할 수 있으며, 이것이 ‘사용하지 않음’을 위한 특별한 문법임을 설명하고 다른 곳에서도 사용할 수 있다고 언급할 수 있습니다. 또는 for 루프에서의 대입을 설명하면서 루프 변수를 사용하지 않는 예를 보여 주고, 그 일부로 소개할 수도 있습니다.

PEP 636은 "_"를 가르치는 방법을 다루는데, 여기서 "_"를 "?"로 바꾸기만 하면 되며, "?"도 다른 문맥에서 비슷하게 사용할 수 있다고 덧붙일 수도 있습니다.

참조 구현

프로토타입 구현은 다음 위치에 있습니다: <https://github.com/Yhg1s/cpython/tree/nonassign>.

기각된 아이디어

미해결 문제

다음 문맥에서도 "?"를 허용해야 합니까:

# imports done for side-effect only.
import os as ?
from os import path as ?

# Function defined for side-effects only (e.g. decorators)
@register_my_func
def ?(...): ...

# Class defined for side-effects only (e.g. decorators, __init_subclass__)
class ?(...): ...

# Parameters defined for unused positional-only arguments:
def f(a, ?, ?): ...
lambda a, ?, ?: ...

# Unused variables with type annotations:
?: int = f()

# Exception handling:
try: ...
except Exception as ?: ...

# With blocks:
with open(f) as ?: ...

이 중 일부는 일관성 관점에서는 타당해 보일 수 있지만, 실제 용도는 제한적이고 미심쩍습니다. "?"에 타입 어노테이션을 붙이는 것이나 "?"를 except 및 with와 함께 사용하는 것은 전혀 의미가 없어 보입니다. 참조 구현에서 except는 지원되지 않지만(기존 문법은 이름만 허용합니다) with는 지원됩니다(기존 문법이 언패킹 대입을 지원하기 때문입니다).

패턴 매칭이 기각되더라도 이 PEP를 수락해야 합니까?