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

Python 개선 제안 한국어 번역

PEP 813 – 예쁜 출력 프로토콜

Author:
Barry Warsaw <barry at python.org>, Eric V. Smith <eric at trueblade.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Created:
07-Nov-2025
Python-Version:
3.16
Post-History:
21-Feb-2026 04-Mar-2026

Table of Contents

번역·라이선스 안내

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

초록

이 PEP는 예쁜 출력(pretty printing)을 더 자유롭게 사용자 정의하고 더 편리하게 쓸 수 있도록 제안된 변경 사항의 모음인 “예쁜 출력 프로토콜”을 설명합니다.

동기

“예쁜 출력”은 가독성을 높이기 위해 객체 표현의 형식을 지정하는 기능입니다. 핵심 기능은 표준 라이브러리 pprint 모듈이 구현합니다. pprint에는 표준 repr() 내장 함수보다 더 읽기 쉬운 객체 표현의 형식을 지정하고 출력하기 위해 사용자가 호출할 수 있는 클래스와 API가 포함되어 있습니다. 중요한 사용 사례로는 디버깅을 위해 큰 딕셔너리와 기타 복잡한 객체를 예쁘게 출력하는 것이 있습니다.

이 PEP는 이 모듈의 기능을 바탕으로 더 많은 사용자 정의 기능과 사용자 편의성을 제공합니다. 또한 Rich 라이브러리의 예쁜 출력 프로토콜에서도 영감을 받았습니다.

근거

예쁜 출력(pretty printing)은 JSON 콘텐츠에서 읽어 들인 딕셔너리처럼 복잡한 자료 구조를 표시하는 데 매우 유용합니다. 그러나 기존 pprint 모듈은 자신이 알고 있는 내장 객체의 형식만 지정할 수 있습니다. 클래스가 자신의 인스턴스가 예쁜 출력에 참여하는 방식을 사용자 정의할 수 있는 방법을 제공하면, 사용자는 특히 디버깅할 때 복잡한 데이터를 시각적으로 더 보기 좋게 표시할 수 있는 선택지를 더 많이 갖게 됩니다.

f-문자열과 str.format()에 !p 변환 지정자를 추가하면 사용자 친화적인 표시를 활용한 디버깅이 한층 더 편리해집니다. 추가 임포트가 필요하지 않으므로, 적어도 가장 흔한 사용 사례에서는 사용자가 익숙한 “print 디버깅” 패턴을 그대로 쉽게 활용할 수 있습니다.

이러한 확장은 독립적으로도, 상호 보완적으로도 동작하여 강력한 새 사용 사례를 제공합니다.

명세

이 제안은 여러 부분으로 구성됩니다.

__pprint__() 메서드

클래스는 새로운 던더 메서드인 __pprint__()를 구현할 수 있으며, 이 메서드가 있으면 인스턴스의 예쁜 출력 표현 중 일부를 생성합니다. 이는 이 제안 이전까지 객체의 사용자 정의 표현을 생성하는 데 쓰이던 유일한 메서드인 __repr__()를 보강합니다. 객체의 repr은 예쁜 출력과는 다른 기능을 제공하므로, 일부 클래스는 자신의 예쁜 표시 방식을 더 세밀하게 제어하고 싶을 수 있습니다. pprint.PrettyPrinter 클래스는 객체에 __pprint__() 메서드가 있으면 이를 따르도록 수정됩니다.

__pprint__()는 선택 사항입니다. 이 메서드가 없으면 표준 예쁜 출력기는 완전한 하위 호환성을 위해 __repr__()로 대체합니다(엄밀히 말하면 pprint.saferepr()가 사용됩니다). 그러나 클래스에 정의된 경우, __pprint__()는 예쁘게 출력할 객체(즉, self) 하나만을 인자로 받습니다.

이 메서드는 값의 시퀀스를 반환하거나 yield해야 하며, 이 값들은 객체의 예쁜 표현을 구성하는 데 사용됩니다. 이 값들은 클래스 이름과 같은 표준 클래스 “장식”으로 감싸집니다. 출력된 표현은 보통 위치 인자, 키워드 인자, 기본값 인자를 갖는 클래스 생성자처럼 보입니다. 값은 다음 형식 중 어느 것이든 될 수 있습니다:

  • 위치 인자를 나타내는 단일 값입니다. 값 자체가 사용됩니다.
  • 키워드 인자를 나타내는 (name, value) 2-튜플입니다. name=value 형태의 표현을 사용합니다. name이 “거짓 같은(false-y)” 값이면 value는 위치 인자로 취급됩니다. 튜플 값을 가진 위치 인자는 이렇게 출력합니다. Examples를 참조하십시오. 그렇지 않은 경우 name은 반드시 정확히 str이어야 합니다.
  • 기본값이 있는 키워드 인자를 나타내는 (name, value, default_value) 3-튜플입니다. value가 default_value와 같으면 이 튜플은 건너뛰고, 그렇지 않으면 name=value를 사용합니다. name은 반드시 정확히 str이어야 합니다.

Note

이 프로토콜은 Rich 라이브러리의 예쁜 출력 프로토콜과 호환됩니다.

f-strings 및 str.format()에 대한 추가 사항

기존의 !s, !r, !a 변환 지정자에 더해 새로운 !p 변환 지정자가 추가됩니다. 표현식 value에 이 지정자를 사용하면 value를 유일한 인자로 전달하여 pprint.pformat()을 호출합니다(필요하면 pprint 모듈을 임포트합니다).

f-문자열에 한해 !p 변환 지정자는 일반적인 구분자 : 뒤에 선택적인 “포맷 명세” 표현식을 받습니다. 예를 들어 f'{obj!p:expression}' 형태입니다. 형식적으로 이 표현식은 단일 인자(포맷할 객체)를 받아 f-문자열 치환 값으로 사용될 문자열을 반환하는 호출 가능 객체로 평가되는 것이라면 무엇이든 될 수 있습니다. 또한 f-문자열에서 !p 지정자는 obj= 형식과 완전히 호환됩니다. 예를 들어 f'{obj=!p:expression}' 형태입니다. 포맷 명세가 주어지지 않으면 위와 같이 pprint.pformat()을 사용합니다.

적어도 이 PEP의 초기 구현에서는 str.format() 호출에서 포맷 명세가 허용되지 않는다는 점에 유의하십시오.

C-API에 대한 추가 사항

!p를 지원하기 위해 새로운 함수 PyObject_Pretty()를 제한된 C API에 추가합니다. 이 함수는 두 개의 인자를 받습니다. 하나는 예쁘게 출력할 객체를 위한 PyObject *이고, 다른 하나는 포매터 호출 가능 객체를 위한 선택적 PyObject *입니다(NULL일 수 있습니다). 포매터가 NULL이면 이 함수는 pprint 모듈을 임포트하고 객체를 인자로 하여 pprint.pformat()을 호출한 뒤 그 결과를 반환합니다. 포매터가 NULL이 아니면 객체를 단일 인자로 받아 문자열을 반환하는 호출 가능 객체여야 합니다. 이는 f'{obj!p:expression}'에서 이미 평가된 :expression을 지원하는 데 사용됩니다.

예시

사용자 정의 __pprint__() 메서드를 사용하여 객체의 표현을 사용자 정의할 수 있습니다. 다음 클래스가 그 예입니다:

class Bass:
    def __init__(self, strings: int, pickups: str, active: bool=False):
        self._strings = strings
        self._pickups = pickups
        self._active = active

    def __pprint__(self):
        yield self._strings
        yield 'pickups', self._pickups
        yield 'active', self._active, False

이제 인스턴스 몇 개를 만들어 예쁘게 출력해 봅시다:

>>> precision = Bass(4, 'split coil P', active=False)
>>> stingray = Bass(5, 'humbucker', active=True)

>>> pprint.pprint(precision)
Bass(4, pickups='split coil P')
>>> pprint.pprint(stingray)
Bass(5, pickups='humbucker', active=True)

!p 변환 지정자는 f-문자열과 str.format()에서 값을 예쁘게 출력하는 데 사용할 수 있습니다:

 >>> print(f'{precision=!p}')
 precision=Bass(4, pickups='split coil P')

 >>> print('{!p}'.format(precision))
 Bass(4, pickups='split coil P')

더 복잡한 객체의 경우 !p를 사용하면 디버깅 출력을 더 읽기 쉽게 만들 수 있습니다:

 >>> import os
 >>> print(os.pathconf_names)
 {'PC_ASYNC_IO': 17, 'PC_CHOWN_RESTRICTED': 7, 'PC_FILESIZEBITS': 18, 'PC_LINK_MAX': 1, 'PC_MAX_CANON': 2, 'PC_MAX_INPUT': 3, 'PC_NAME_MAX': 4, 'PC_NO_TRUNC': 8, 'PC_PATH_MAX': 5, 'PC_PIPE_BUF': 6, 'PC_PRIO_IO': 19, 'PC_SYNC_IO': 25, 'PC_VDISABLE': 9, 'PC_MIN_HOLE_SIZE': 27, 'PC_ALLOC_SIZE_MIN': 16, 'PC_REC_INCR_XFER_SIZE': 20, 'PC_REC_MAX_XFER_SIZE': 21, 'PC_REC_MIN_XFER_SIZE': 22, 'PC_REC_XFER_ALIGN': 23, 'PC_SYMLINK_MAX': 24}

 >>> print(f'{os.pathconf_names = !p}')
 os.pathconf_names = {'PC_ALLOC_SIZE_MIN': 16,
  'PC_ASYNC_IO': 17,
  'PC_CHOWN_RESTRICTED': 7,
  'PC_FILESIZEBITS': 18,
  'PC_LINK_MAX': 1,
  'PC_MAX_CANON': 2,
  'PC_MAX_INPUT': 3,
  'PC_MIN_HOLE_SIZE': 27,
  'PC_NAME_MAX': 4,
  'PC_NO_TRUNC': 8,
  'PC_PATH_MAX': 5,
  'PC_PIPE_BUF': 6,
  'PC_PRIO_IO': 19,
  'PC_REC_INCR_XFER_SIZE': 20,
  'PC_REC_MAX_XFER_SIZE': 21,
  'PC_REC_MIN_XFER_SIZE': 22,
  'PC_REC_XFER_ALIGN': 23,
  'PC_SYMLINK_MAX': 24,
  'PC_SYNC_IO': 25,
  'PC_VDISABLE': 9}

f-문자열에 한해 !p 변환 지정자는 포맷 명세 표현식도 받으며, 이 표현식은 단일 인자를 받아 문자열을 반환하는 호출 가능 객체로 평가되어야 합니다:

 >>> def slappa(da: Bass) -> str:
 ...     return 'All about that bass'

 >>> print(f'{precision=!p:slappa}')
 precision=All about that bass

다음은 위치 인자가 튜플 값을 가지는 예입니다. 이 경우 name을 “거짓 같은(false-y)” 값으로 하여 2-튜플 형식을 사용합니다.

>>> class Things:
...     def __pprint__(self):
...         yield (None, (1, 2))
...         yield ('', (3, 4))
...         yield ('arg', (5, 6))
...
>>> from rich.pretty import pprint
>>> pprint(Things())
Things((1, 2), (3, 4), arg=(5, 6))

하위 호환성

새 기능을 전혀 사용하지 않으면 이 PEP는 완전한 하위 호환성을 유지합니다.

보안 영향

이 제안에는 알려진 보안 영향이 없습니다.

교육 방법

pprint 모듈, f-문자열, str.format()에 문서와 예제가 추가됩니다. 초보자는 자신의 객체를 더 보기 좋게 표현하고 싶어지기 전까지는 이 새로운 기능들을 배울 필요가 없습니다.

참조 구현

참조 구현은 현재 CPython main 브랜치의 PEP 저자 브랜치로 제공됩니다.

기각된 아이디어

__pprint__() 반환값에 대해 namedtuple(), dataclasses 또는 덕 타이핑된 인스턴스를 반환 타입으로 사용하는 대안적인 명세도 검토했습니다. 결국 저희는 이 대안을 거부했습니다. 단지 이 함수에서 반환값을 돌려주기 위해 사람들이 새 클래스를 정의하거나 임포트를 추가하도록 강요하고 싶지 않았기 때문입니다.

보류된 아이디어

향후 t-문자열에 !p 변환 지원을 추가할 수 있습니다. str.format()의 !p 변환에 :expression 형식을 추가하는 것도 보류되었습니다.

미해결 문제

Rich 호환성

출력 형식과 API는 Rich에서 많은 영감을 받았습니다. Rich가 !p:callable과 호환되는 호출 가능 객체를 꽤 쉽게 구현할 수 있으리라는 것이 기본 발상입니다. Rich의 API는 인스턴스를 생성자와 비슷한 형태로 표현해 출력하도록 설계되어 있으므로, 인자를 둘러싼 “클래스 장식”의 상당 부분을 제어할 수 없습니다. Rich는 rich repr 메서드에 .angular=True 속성을 설정하면 괄호 대신 꺾쇠괄호(즉, <...>)를 사용하는 것도 지원합니다. 이 PEP는 해당 기능을 지원하지 않지만, 향후에는 지원할 수 있을 것입니다.

이는 또한 문자열, 딕셔너리, 리스트 등과 같은 내장 타입의 예쁜 출력 형식을 제어할 방법이 없다는 뜻이기도 합니다. pprint는 Rich만큼 기능이 풍부(rich)하도록(말장난입니다!) 의도된 것이 아니므로 이는 괜찮아 보입니다. 이 PEP는 그러한 화려한 기능을 의도적으로 범위 밖으로 간주합니다.

감사의 글

PEP 작성자들이 f-문자열에서 !p:callable을 사용하는 프로토타입을 만들고 그 실현 가능성을 입증하도록 도와준 Pablo Galindo Salgado.

각주

현재로서는 없습니다.

변경 이력

  • 04-Mar-2026
    • f-문자열에서만(str.format()은 제외) !p 변환 지정자가 선택적인 “형식 명세”를 받습니다.
    • 이 PEP는 더 이상 print() 내장 함수에 pretty 인자를 추가하자고 제안하지 않습니다. f-문자열에 !p:callable 문법이 추가되었으므로 새 인자는 필요하지 않습니다.
    • 튜플을 위치 인자로 예쁘게 출력하려면 2-튜플 값 형식을 사용하고, 인자 이름으로 “거짓으로 평가되는(false-y)” 값을 전달하도록 명시합니다.
    • 참으로 평가되는 name은 반드시 str이어야 함을 명확히 합니다.
    • f-문자열과 str.format()의 !p 변환이 암묵적으로 pprint 모듈을 임포트한다는 점을 명시합니다.
    • 새로운 제한된 C API 함수 PyObject_Pretty()를 설명하고, 선택적 인자를 추가합니다.