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

Python 개선 제안 한국어 번역

PEP 3140 – 컨테이너에 대한 str(container)는 repr(item)이 아니라 str(item)을 호출해야 합니다

Author:
Oleg Broytman <phd at phdru.name>, Jim J. Jewett <jimjjewett at gmail.com>
Discussions-To:
Python-3000 list
Status:
Rejected
Type:
Standards Track
Created:
27-May-2008
Post-History:
28-May-2008

Table of Contents

번역·라이선스 안내

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

거부

Guido는 이 변경이 베타에 너무 임박한 시점에 너무 큰 혼란을 일으킬 것이라고 말했습니다. [1]을 참조하십시오.

초록

이 문서는 str(container)의 현재 구현이 갖는 장점과 단점을 논의합니다. 또한 다른 접근 방식, 즉 repr(item) 대신 str(item)을 호출하는 방식의 장단점도 논의합니다.

동기

현재 str(container)는 항목에 대해 repr을 호출합니다. 찬성 측 인자:

  • 컨테이너는 사용자가 str(container)에서 무엇을 보고 싶어 하는지(둘러싸는 기호, 구분자 등) 추측하기를 거부합니다.
  • repr(item)은 보통 타입 정보를 표시합니다. 문자열을 둘러싼 따옴표, 클래스 이름 등이 그 예입니다.

반대 측 인자:

  • 비논리적입니다. str()은 __str__이 존재하면 __repr__이 아니라 __str__을 호출할 것으로 기대됩니다.
  • 항목의 __str__을 호출하여 컨테이너의 내용을 출력하는 표준 방법이 없으며, 이는 __str__과 __repr__이 서로 다른 결과를 반환하는 경우에 불편합니다.
  • repr(item)은 때때로 잘못된 동작을 합니다(예를 들어 비 ASCII 문자열을 16진수로 이스케이프합니다).

이 PEP는 str(container)의 동작 방식을 변경할 것을 제안합니다. 한 가지 세부 사항, 즉 항목에 대해 repr 대신 str을 호출한다는 점을 제외하고는 repr(container)의 동작 방식을 그대로 따를 것을 제안합니다. 이렇게 하면 사용자는 item.__repr__과 item.__str__ 중 어느 쪽의 결과를 얻을지 선택할 수 있습니다.

현재 상황

대부분의 컨테이너 타입(튜플, 리스트, 딕셔너리, 집합 등)은 __str__ 메서드를 구현하지 않으므로 str(container)는 container.__repr__을 호출하며, container.__repr__은 일단 호출되고 나면 자신이 str에서 호출되었다는 사실을 잊고 컨테이너의 항목에 대해 항상 repr을 호출합니다.

이 동작에는 장점과 단점이 있습니다. 한 가지 장점은 대부분의 항목이 타입 정보와 함께 표현된다는 것입니다. 문자열은 따옴표로 둘러싸이고, 인스턴스는 클래스 이름과 인스턴스 데이터를 모두 가질 수 있습니다.:

>>> print([42, '42'])
[42, '42']
>>> print([Decimal('42'), datetime.now()])
[Decimal("42"), datetime.datetime(2008, 5, 27, 19, 57, 43, 485028)]

단점은 __repr__이 종종 기술적인 데이터(’<object at address>’ 같은)나 읽을 수 없는 문자열(입력이 비 ASCII 문자열인 경우 16진수로 인코딩된 문자열)을 반환한다는 것입니다.:

>>> print(['тест'])
['\xd4\xc5\xd3\xd4']

PEP 3138의 동기 중 하나는 repr과 str 모두 키가 비 ASCII 텍스트 문자열인 딕셔너리를 합리적으로 출력하지 못한다는 점입니다. 이제 유니코드 식별자가 허용되었으므로, 여기에는 Python 자체의 속성 딕셔너리도 포함됩니다. JSON 직렬화도 여기에 포함됩니다(그리고 json 라이브러리에 몇 가지 번거로운 우회를 초래했습니다).

PEP 3138은 “repr은 안전한 ASCII이다”라는 불변 조건을 깨고, (영속화에 사용되는) repr이 일부 객체를 출력하는 방식을 변경하여 이를 해결할 것을 제안하지만, 이 방식은 시스템에 따라 실패할 수 있습니다.

str(container)의 동작 방식을 변경하면 일반적인 경우에는 디버깅이 쉬워지고, 기계가 읽는 경우에는 ASCII 전용이라는 안전성이 유지됩니다. 유일한 단점은 str(x)와 repr(x)가 더 자주 달라진다는 것입니다. 하지만 이는 현재의 거의 동일한 버전으로는 충분하지 않은 경우에 한합니다.

또한 str(container)가 항목에 대해 str 대신 repr을 호출하는 것은 비논리적으로 보입니다. 다음 코드에 대해 이렇게 기대하는 것은 지극히 논리적입니다.:

class Test:
    def __str__(self):
        return "STR"

    def __repr__(self):
        return "REPR"


test = Test()
print(test)
print(repr(test))
print([test])
print(str([test]))

출력하려면:

STR
REPR
[STR]
[STR]

실제로 출력되는 결과는:

STR
REPR
[REPR]
[REPR]

특히 Python 2의 print가 튜플처럼 보이는 것에 대해 호출되면 str을 사용한다는 점은 비논리적입니다:

>>> print Decimal('42'), datetime.now()
42 2008-05-27 20:16:22.534285

반면 실제 튜플에 대해서는 다음과 같이 출력합니다:

>>> print((Decimal('42'), datetime.now()))
(Decimal("42"), datetime.datetime(2008, 5, 27, 20, 16, 27, 937911))

다른 접근 방식 - str(item)을 호출합니다

예를 들어 숫자의 경우 사람들이 관심을 두는 것은 대개 값뿐입니다.

>>> print Decimal('3')
3

그러나 값을 리스트에 넣으면 마치 기계를 위해 repr이 호출된 것처럼 사용자가 타입 정보까지 읽어야 합니다:

>>> print [Decimal('3')]
[Decimal("3")]

이 변경 후에는 타입 정보가 str 출력을 어지럽히지 않을 것입니다:

>>> print "%s".format([Decimal('3')])
[3]
>>> str([Decimal('3')])  # ==
[3]

그러나 원한다면 여전히 타입 정보를 얻을 수 있습니다:

>>> print "%r".format([Decimal('3')])
[Decimal('3')]
>>> repr([Decimal('3')])  # ==
[Decimal('3')]

이 문제를 해결하는 전략은 여러 가지가 있습니다. 가장 급진적인 전략은 __repr__이 “str에서 호출되었으므로 항목에 대해 repr이 아니라 str을 호출할 것”이라는 새 매개변수(플래그)를 받도록 변경하는 것입니다. 이 제안의 단점은 모든 __repr__ 구현을 변경해야 한다는 것입니다. 인트로스펙션이 약간 도움이 될 수 있지만(호출하기 전에 __repr__이 매개변수를 2개 받는지 3개 받는지 검사), 모든 내장 컨테이너처럼 C로 작성된 클래스에서는 인트로스펙션이 동작하지 않습니다.

덜 급진적인 제안은 내장 컨테이너 타입에 __str__ 메서드를 구현하는 것입니다. 명백한 단점은 노력이 중복된다는 것입니다. 이 모든 __str__과 __repr__ 구현은 항목에 대해 str을 호출하는지 repr을 호출하는지라는 작은 세부 사항 하나만 다를 뿐입니다.

가장 보수적인 제안은 str을 전혀 변경하지 않고, 개발자가 애플리케이션이나 라이브러리에 특화된 자체 예쁜 출력기(pretty-printer)를 구현할 수 있게 하는 것입니다. 단점은 역시 노력이 곱셈처럼 중복되고, 작고 특수한 컨테이너 순회 알고리즘이 수없이 난립한다는 것입니다.

하위 호환성

타입 정보가 평소보다 더 중요한 경우에는 repr을 명시적으로 호출하여 여전히 현재와 같은 결과를 얻을 수 있습니다.

참고 문헌