Python Language
클래스 인스턴스의 문자열 표현 : __str__ 및 __repr__ 메소드
수색…
비고
두 가지 방법 모두 구현에 대한 참고 사항
두 메소드가 모두 구현 될 때 인간 친화적 인 표현 (예 : "Ace of Spaces")을 반환하는 __str__
메소드를 사용하는 것이 일반적이며 __repr__
은 eval
친숙한 표현을 반환합니다.
사실, repr()
대한 Python 문서는 정확히 다음과 같은 내용을 기록합니다.
많은 함수에서이 함수는 eval ()에 전달 될 때 동일한 값을 가진 객체를 반환하는 문자열을 반환하려고 시도합니다. 그렇지 않으면 표현은 함께 객체 유형의 이름이 포함 된 꺾쇠 괄호로 묶인 문자열입니다 종종 개체의 이름과 주소를 포함하는 추가 정보가 있습니다.
이것이 의미하는 것은 __str__
이 이전에 보여준 것처럼 "Ace of Spaces"와 같은 것을 반환하도록 구현 될 수 있다는 것입니다. __repr__
은 대신 Card('Spades', 1)
를 반환하기 위해 구현 될 수 있습니다.
이 문자열은 다소 "왕복"형태로 eval
에 직접 전달 될 수 있습니다.
object -> string -> object
그러한 메소드의 구현 예는 다음과 같습니다.
def __repr__(self):
return "Card(%s, %d)" % (self.suit, self.pips)
노트
[1]이 출력은 구현에 따라 다릅니다. 표시된 문자열은 cpython에서 가져온 것입니다.
[2] 당신은 이미이 str()
/ repr()
의 결과를 나누어 그것을 알지 못했을 수도 있습니다. 백 슬래시와 같은 특수 문자가 포함 된 문자열을 str()
통해 문자열로 변환하면 백 슬래시가있는 것처럼 나타납니다 (한 번 나타남). repr()
통해 문자열로 변환되면 (예 : 목록의 요소가 표시됨) 백 슬래시가 이스케이프되므로 두 번 나타납니다.
자극
따라서 방금 재생 카드를 캡슐화하는 깔끔하고 작은 클래스 인 Python으로 첫 번째 수업을 만들었습니다.
class Card:
def __init__(self, suit, pips):
self.suit = suit
self.pips = pips
코드의 다른 부분에서이 클래스의 몇 가지 인스턴스를 만듭니다.
ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs', 4)
six_of_hearts = Card('Hearts', 6)
"손"을 표현하기 위해 카드 목록을 만들었습니다.
my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]
이제 디버깅하는 동안 손이 어떻게 보이는지보고 싶으므로 자연스럽게 나오고 작성하는 작업을 수행하십시오.
print(my_hand)
그러나 당신이 돌아 오는 것은 횡설수설의 무리 다 :
[<__main__.Card instance at 0x0000000002533788>,
<__main__.Card instance at 0x00000000025B95C8>,
<__main__.Card instance at 0x00000000025FF508>]
혼란 스러워요, 당신은 단지 하나의 카드를 인쇄하려고합니다 :
print(ace_of_spades)
그리고 다시, 당신은이 이상한 결과를 얻습니다.
<__main__.Card instance at 0x0000000002533788>
두려워하지 마라. 이 문제를 해결하려고합니다.
우선, 여기서 일어나는 일을 이해하는 것이 중요합니다. print(ace_of_spades)
를 쓸 때 파이썬에게 코드에서 ace_of_spades
호출하는 Card
인스턴스에 대한 정보를 인쇄하기를 원했습니다. 그리고 공정하게, 그것은했다.
이 출력은 두 가지 중요한 비트로 구성됩니다. 객체의 type
과 객체의 id
입니다. 두 번째 부분 만 (16 진수) print
호출시 개체를 고유하게 식별하기에 충분합니다. [1]
실제로 진행된 것은 여러분이 파이썬에게 그 객체의 본질을 "말로 표현"하고 그것을 여러분에게 보여달라고 요청한 것입니다. 동일한 기계류의보다 명확한 버전은 다음과 같습니다.
string_of_card = str(ace_of_spades)
print(string_of_card)
첫 번째 줄에서는 Card
인스턴스를 문자열로 변환하고 두 번째 인스턴스에서는 문자열을 표시하려고합니다.
문제
마주 치게되는 문제는 파이썬에게 Card
를 만드는 데 필요한 Card
클래스에 대해 알아야 할 모든 것을 말했지만 Card
인스턴스를 문자열로 변환하는 방법을 알려주지 않았기 때문에 발생합니다.
그리고 그것은 몰랐기 때문에 여러분이 (암시 적으로) str(ace_of_spades)
쓸 때, 그것은 여러분이 보았던 것, 즉 Card
인스턴스의 일반적인 표현을 여러분에게주었습니다.
해결책 (1 부)
그러나 우리는 우리가 어떻게 커스텀 클래스의 인스턴스를 문자열로 변환 할 것인지 파이썬에게 말할 수 있습니다 . 그리고 우리가하는 일은 __str__
"dunder"(이중 밑줄) 또는 "magic"방법입니다.
파이썬에게 클래스 인스턴스로부터 문자열을 생성하라고 말할 때마다, 클래스에서 __str__
를 찾아서 호출 할 것이다.
다음과 같이 업데이트 된 Card
클래스를 고려하십시오.
class Card:
def __init__(self, suit, pips):
self.suit = suit
self.pips = pips
def __str__(self):
special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}
card_name = special_names.get(self.pips, str(self.pips))
return "%s of %s" % (card_name, self.suit)
이제 우리는 Card
클래스에서 __str__
메서드를 정의했습니다.이 메서드는 얼굴 카드에 대한 간단한 사전 검색 후에 형식이 지정된 문자열을 반환 하지만, 우리는 결정합니다.
( "return"은 문자열을 반환하는 것의 중요성을 강조하기 위해 여기에 굵게 표시되어 있습니다. 단순히 인쇄하는 것만으로는 인쇄되지 않을 수도 있지만 str(ace_of_spades)
, 주 프로그램에서 print 함수 호출을하지 않아도된다. 그래서 명확하게하려면 __str__
이 문자열을 반환하는지 확인해야한다.
__str__
메서드는 메서드이므로 첫 번째 인수는 self
되며 인수를 수락하거나 인수를 전달해서는 안됩니다.
카드를보다 사용자 친화적 인 방법으로 표시하는 문제로 돌아가서 다시 실행하면
ace_of_spades = Card('Spades', 1)
print(ace_of_spades)
우리는 우리의 산출물이 훨씬 더 좋음을 보게 될 것입니다 :
Ace of Spades
좋아, 끝났어. 그렇지?
우리 기지를 커버하기 위해, Card
인스턴스의 목록 인 hand
인쇄하면서 우리가 직면했던 첫 번째 문제를 해결했는지 다시 한번 확인합시다.
따라서 다음 코드를 다시 확인하십시오.
my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]
print(my_hand)
놀랍게도 재미있는 16 진수 코드를 다시 얻습니다.
[<__main__.Card instance at 0x00000000026F95C8>,
<__main__.Card instance at 0x000000000273F4C8>,
<__main__.Card instance at 0x0000000002732E08>]
무슨 일이야? 파이썬에게 우리의 Card
인스턴스가 어떻게 표시되기를 원하는지, 왜 분명히 잊어 버린 것 같습니까?
해결책 (2 부)
글쎄, 비하인드 머신은 파이썬이 목록에있는 항목의 문자열 표현을 원할 때 약간 다릅니다. 파이썬은이 목적으로 __str__
을 신경 쓰지 않습니다.
대신 다른 방법을 찾습니다 __repr__
, 그 발견되어 있지 않은 경우, 그것은 다시 "16 진수 것"에 떨어진다. [2]
그래서 나는 똑같은 일을하는 두 가지 방법을 만들어야한다는 말입니까? 하나는 내 카드를 print
하고 다른 하나는 컨테이너에 print
할 때 사용하는 카드입니까?
아니요. 우선 __str__
및 __repr__
메서드를 모두 구현하는 경우 클래스 가 어떻게 될지 살펴 보겠습니다.
class Card:
special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}
def __init__(self, suit, pips):
self.suit = suit
self.pips = pips
def __str__(self):
card_name = Card.special_names.get(self.pips, str(self.pips))
return "%s of %s (S)" % (card_name, self.suit)
def __repr__(self):
card_name = Card.special_names.get(self.pips, str(self.pips))
return "%s of %s (R)" % (card_name, self.suit)
여기서는 __str__
과 __repr__
의 두 메소드를 구별하기 위해 두 메소드를 구별하기 위해 __str__
반환 한 문자열에 (S)
를 추가하고 __repr__
반환 한 문자열에 (R)
을 추가합니다.
__str__
메서드와 마찬가지로 __repr__
도 인수를받지 않고 문자열을 반환 합니다.
이제 우리는 각각의 경우에 어떤 방법으로 책임이 있는지를 볼 수 있습니다.
ace_of_spades = Card('Spades', 1)
four_of_clubs = Card('Clubs', 4)
six_of_hearts = Card('Hearts', 6)
my_hand = [ace_of_spades, four_of_clubs, six_of_hearts]
print(my_hand) # [Ace of Spades (R), 4 of Clubs (R), 6 of Hearts (R)]
print(ace_of_spades) # Ace of Spades (S)
덮었다으로, __str__
우리가 통과 할 때 메서드가 호출 된 Card
에 인스턴스를 print
하고 __repr__
우리가 우리의 인스턴스의 목록 통과 할 때 메소드가 호출 된 print
.
이 시점에서 str()
을 사용하여 커스텀 클래스 인스턴스로부터 명시 적으로 스트링을 생성 할 수있는 것처럼 우리는 명시 적으로 클래스의 스트링 표현 을 repr()
이라는 내장 함수로 생성 할 수있다. repr()
.
예 :
str_card = str(four_of_clubs)
print(str_card) # 4 of Clubs (S)
repr_card = repr(four_of_clubs)
print(repr_card) # 4 of Clubs (R)
추가적으로 정의 된 경우, 메소드를 직접 호출 할 수 있습니다 (약간은 불분명하고 불필요한 것으로 보이지만).
print(four_of_clubs.__str__()) # 4 of Clubs (S)
print(four_of_clubs.__repr__()) # 4 of Clubs (R)
그 복제 된 기능들에 관해서 ...
파이썬 개발자는 str()
과 repr()
에서 동일한 문자열을 반환하기를 원했을 때 함수를 복제하여 메서드를 복제해야한다는 것을 알았습니다.
그래서 대신에 그 필요성을 제거하는 메커니즘이 있습니다. 하나는 너를이 시점까지 지나치게 snuck했다. 클래스가 __repr__
메소드를 구현 하지만 __str__
메소드를 구현 하지 않고 해당 클래스의 인스턴스를 str()
(암시 적 또는 명시 적이든간에)에 전달하면 Python이 __repr__
구현에서 대체되어이를 사용합니다.
따라서, 다음과 같은 버전의 Card
클래스를 고려해보십시오.
class Card:
special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}
def __init__(self, suit, pips):
self.suit = suit
self.pips = pips
def __repr__(self):
card_name = Card.special_names.get(self.pips, str(self.pips))
return "%s of %s" % (card_name, self.suit)
이 버전은 __repr__
메소드 만 구현합니다. 그럼에도 불구하고 str()
호출하면 사용자 친화적 인 버전이됩니다.
print(six_of_hearts) # 6 of Hearts (implicit conversion)
print(str(six_of_hearts)) # 6 of Hearts (explicit conversion)
repr()
호출도 마찬가지입니다.
print([six_of_hearts]) #[6 of Hearts] (implicit conversion)
print(repr(six_of_hearts)) # 6 of Hearts (explicit conversion)
개요
클래스 인스턴스가 사용자 친화적 인 방식으로 "스스로 표시"하도록 권한을 부여하려면 적어도 클래스의 __repr__
메소드를 구현하는 것이 좋습니다. 메모리가 작동한다면 Raymond Hettinger는 클래스가 __repr__
구현한다는 것이 파이썬 코드 리뷰를 할 때 가장 먼저 찾은 것 중 하나라고 말했고 지금까지는 그 이유를 분명히해야합니다. 간단한 방법으로 디버깅 문, 충돌 보고서 또는 로그 파일에 추가 할 수 있는 정보의 양은 기본적으로 제공되는 유용하지 않은 (유형, ID) 정보와 비교할 때 압도적입니다.
예를 들어 컨테이너 내부에서 다른 표현을 원한다면 __repr__
및 __str__
메소드를 모두 구현해야합니다. (아래 두 가지 방법을 어떻게 다르게 사용하는지 더 자세히).
두 메소드 모두 구현, eval-round-trip 스타일 __repr __ ()
class Card:
special_names = {1:'Ace', 11:'Jack', 12:'Queen', 13:'King'}
def __init__(self, suit, pips):
self.suit = suit
self.pips = pips
# Called when instance is converted to a string via str()
# Examples:
# print(card1)
# print(str(card1)
def __str__(self):
card_name = Card.special_names.get(self.pips, str(self.pips))
return "%s of %s" % (card_name, self.suit)
# Called when instance is converted to a string via repr()
# Examples:
# print([card1, card2, card3])
# print(repr(card1))
def __repr__(self):
return "Card(%s, %d)" % (self.suit, self.pips)