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

Python 개선 제안 한국어 번역

PEP 682 – 부호 있는 0을 위한 형식 지정자

Author:
John Belmonte <john at neggie.net>
Sponsor:
Mark Dickinson <dickinsm at gmail.com>
PEP-Delegate:
Mark Dickinson
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
29-Jan-2022
Python-Version:
3.11
Post-History:
08-Feb-2022
Resolution:
Discourse thread

Table of Contents

번역·라이선스 안내

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

초록

float와 Decimal 타입은 signed zero를 표현할 수 있지만, 수학의 많은 필드에서 음의 영은 놀랍거나 원치 않는 것입니다. 특히 (흔히 반올림된) 수치 결과를 표시하는 맥락에서는 더욱 그렇습니다. 이 PEP는 음수 0을 양수 0으로 정규화할 수 있도록 string format specification을 확장할 것을 제안합니다.

동기

다음은 음수 0입니다:

>>> x = -0.
>>> x
-0.0

숫자를 포맷할 때 반올림으로 인해 음수 0이 생길 수 있습니다. 사용자의 의도가 정말로 정밀도를 버리는 것이라고 가정하면, 반올림된 결과에서 음수 0과 양수 0을 구분하는 것은 원치 않는 부산물로 여겨질 수 있습니다:

>>> for x in (.002, -.001, .060):
...     print(f'{x: .1f}')
 0.0
-0.0
 0.1

음수 0의 부호를 지우는 방법에는 여러 가지가 있습니다. 양수 0을 더하면 조건문 없이 이를 달성할 수 있습니다:

>>> x = -0.
>>> x + 0.
0.0

포맷할 때 음수 0을 정규화하려면 입력값을 미리 반올림하는 중복된 (그리고 오류가 발생하기 쉬운) 작업을 수행해야 합니다:

>>> for x in (.002, -.001, .060):
...     print(f'{round(x, 1) + 0.: .1f}')
 0.0
 0.0
 0.1

언어와 관계없이 프로그래머들이 음수 0을 억제하는 방법을 자주 찾고 있으며, 결국 다양한 우회 방법(사전 반올림, 사후 정규식 처리 등)에 이르게 된다는 증거는 충분합니다. 몇 가지 예는 다음과 같습니다:

대신 우리가 원하는 것은 숫자 문자열 포맷이 이미 제공하는 다른 모든 기능에 더해, 음수 0을 정규화하는 일급 옵션입니다.

근거

포맷된 숫자 출력에서 음수 0을 원치 않는 사용 사례가 있으며, 오히려 원치 않는 경우가 더 흔하다고 볼 수도 있습니다. 숫자 포맷에는 이미 반올림이 포함되어 있고 음수 0의 정규화는 반드시 반올림 후에 이루어져야 하므로, 포맷 명세를 확장하는 것이 이를 지원하는 가장 좋은 방법입니다.

포맷하기 전에 숫자를 미리 반올림하고 정규화하는 것도 가능하지만, 이는 번거롭고, 그 반올림이 포맷 명세의 반올림과 정확히 일치하지 않으면 오류가 발생하기 쉽습니다. 게다가 포맷을 감싸는 함수들은 정밀도 정보를 추출하기 위해 포맷 명세를 파싱해야 하는 처지에 놓이게 됩니다. 예를 들어, 1차원 숫자 배열을 포맷하는 다음 유틸리티가 이러한 사전 반올림 때문에 얼마나 복잡해질지 생각해 보십시오:

def format_vector(v, format_spec='8.2f'):
    """Format a vector (any iterable) using given per-term format string."""
    return f"[{','.join(f'{term:{format_spec}}' for term in v)}]"

현재까지 음수 0에 대한 포맷 옵션을 제공하는, 널리 사용되는 다른 언어나 라이브러리는 없는 것으로 보입니다. 다만 아래에 명세된 것과 동일한 z 옵션 문법과 의미 체계가 C++ std::format()에 대해 제안된 바 있습니다(proposed for C++ std::format()). 이 제안은 C++20에서는 철회되었지만, C++23에서는 합의된 제안이 나올 예정입니다. (이 PEP를 촉발한 원래의 feature request는 C++ 제안을 알지 못한 채 제기되었습니다.)

Rust 개발자들은 print 출력에서 음수 0을 억제할지 논의할 때 소규모로 survey of other languages를 수행했습니다. 특히 그 조사에서는 음수 0 처리 옵션을 제공하는 언어를 하나도 언급하지 않았습니다.

명세

Format Specification Mini-Language의 sign 다음에 선택적인 리터럴 z가 추가됩니다:

[[fill]align][sign][z][#][0][width][grouping_option][.precision][type]

여기서 z는 부동소수점 표현 타입(형식 명세 문서에 정의된 f, g 등)에 허용됩니다. z는 각 숫자 타입의 .__format__() 메서드가 지원하므로, f-문자열, 내장 format(), str.format()에서 이 지정자를 사용할 수 있습니다.

z가 있으면 음의 영(원래 값이든 반올림의 결과이든)은 양의 영으로 정규화됩니다.

개요:

>>> x = -.00001
>>> f'{x:z.1f}'
'0.0'

>>> x = decimal.Decimal('-.00001')
>>> '{:+z.1f}'.format(x)
'+0.0'

설계 노트

숫자를 포매팅할 때 음의 영을 기대하거나 그에 의존하는 프로그램의 동작을 바꿀 수는 없으므로, 이 해결책은 명시적으로 선택해야만 적용되는 옵트인 방식이어야 합니다.

제안된 확장은 의도적으로 [sign[z]]가 아니라 [sign][z]입니다. sign의 기본값(-)은 널리 알려져 있지 않고 명시적으로 쓰이지도 않으므로, 이렇게 하면 z 옵션을 쓰려고 모든 사람이 그 기본값을 배워야 하는 일을 피할 수 있습니다.

f-문자열, 내장 format(), str.format()은 새 옵션을 사용할 수 있지만, %-포매팅은 사용할 수 없습니다. , 옵션(PEP 378)의 경우처럼, %-포매팅을 새 옵션으로 확장하지 않은 선례가 이미 있습니다.

C99 printf는 이미 z 옵션 문자를 다른 목적으로 사용합니다. 바로 부호 없는 타입(u)을 size_t의 길이에 맞추도록 한정하는 용도입니다. 하지만 부호 있는 영 옵션은 정수 표현 타입에 z를 쓰는 것을 명시적으로 금지하므로, C가 이 새 옵션을 채택하려 한다면 두 용도를 구분할 수 있습니다.

하위 호환성

새 포매팅 동작은 명시적으로 선택해야만 적용되므로, 기존 프로그램의 숫자 포매팅은 영향을 받지 않습니다.

교육 방법

일반적인 Python 입문 과정에서는 문자열 형식화를 아주 자세히 다루지 않습니다. 그런 과정에서는 아무것도 조정할 필요가 없습니다. 문자열 형식 명세를 자세히 다루는 과정이라면, 포매팅으로 영으로 반올림되는 음수 값에 z 옵션이 미치는 효과를 보여 주는 예제 하나면 충분합니다. 다른 사람의 코드에서 이 기능을 접한 개별 개발자라면, 라이브러리 레퍼런스 매뉴얼의 Format Specification Mini-Language 섹션을 참고하는 것으로 충분합니다.

참조 구현

참조 구현은 pull request #30049에 있습니다.