Python Language
クラスインスタンスの文字列表現:__str__および__repr__メソッド
サーチ…
備考
両方のメソッドの実装に関する注意
両方の方法が実装されている場合は、それが持っている、やや一般的です__str__
人に優しい表現(例えば、「スペースのエース」)を返し、メソッド__repr__
返すeval
やさしい表現を。
実際、 repr()
Pythonドキュメントはこれを正確にメモしています:
多くの型では、この関数はeval()に渡されるときに同じ値を持つオブジェクトを生成する文字列を返そうとします。さもなければ、その表現はオブジェクトの型の名前を含む山括弧で囲まれた文字列です多くの場合、オブジェクトの名前とアドレスを含む追加情報があります。
つまり、 __str__
実装して__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()
を介して文字列に変換されると、バックスラッシュはそのまま表示されstr()
一度表示されます)。それらがrepr()
を介して文字列に変換されると(たとえば、表示されるリストの要素として)、バックスラッシュはエスケープされて2回表示されます。
動機
だから、ちょうどあなたの最初のクラスを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>]
混乱していると、1枚のカードだけを印刷しようとします:
print(ace_of_spades)
そしてもう一度、あなたはこの奇妙な結果を得ます:
<__main__.Card instance at 0x0000000002533788>
何も怖くない。これを直すつもりです。
まず、ここで何が起こっているのかを理解することが重要です。あなたがprint(ace_of_spades)
を書いたとき、Pythonにあなたのコードがace_of_spades
呼び出しているCard
インスタンスに関する情報を出力したかったとace_of_spades
。そして、公正であるために、それはしました。
その出力は、オブジェクトのtype
とオブジェクトのid
の2つの重要なビットで構成されます。 2番目の部分だけ(16進数)は、 print
呼び出し時にオブジェクトを一意に識別するのに十分です。[1]
本当に続いたのは、Pythonにオブジェクトの本質を "言葉で伝え"て、それをあなたに表示するように頼んだことでした。同じマシンのより明示的なバージョンは、次のようなものです。
string_of_card = str(ace_of_spades)
print(string_of_card)
最初の行では、 Card
インスタンスを文字列に変換し、次にインスタンスを表示します。
問題
あなたが遭遇している問題は、あなたがPythonにCard
を作成するためのCard
クラスについて知っておく必要があるすべてのことについて、あなたがCard
インスタンスを文字列に変換する方法を教えていないという事実のために起こります。
そしてそれがわからないので、(暗黙のうちに)あなたがstr(ace_of_spades)
書いたとき、それはあなたが見たもの、つまりCard
インスタンスの一般的な表現を与えました。
解決策(その1)
しかし、カスタムクラスのインスタンスをどのように文字列に変換するかをPythonに伝えることができます。そして、私たちがこれを行う方法は、 __str__
"dunder"(ダブルアンダースコアの場合)または "magic"メソッドの場合です。
クラスインスタンスから文字列を作成するようにPythonに指示するたびに、クラスで__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)
ここでは、 __str__
メソッドをCard
クラスに定義しました。これは、簡単な辞書検索の後、フォーマットされた文字列を返します 。
(文字列を返すことの重要性を強調するために、 "返品"はここでは太字で示していますが、単純に印刷するのではなく、印刷すると思われるかもしれませんが、 str(ace_of_spades)
、あなたのメインプログラムでprint関数の呼び出しをしなくても、 __str__
が文字列を返すことを確認して__str__
。
__str__
メソッドはメソッドなので、最初の引数はself
あり、 __str__
引数を受け入れることも渡すべきでもありません。
カードをよりユーザーフレンドリーな方法で表示するという問題に戻り、再び実行すると:
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>]
どうしたの?私たちは、Pythonに、 Card
インスタンスを表示する方法を教えてくれました。どうして明らかに忘れてしまったのでしょうか?
解決策(その2)
まあ、舞台裏の機械は、Pythonがリスト内の項目の文字列表現を取得したいときには少し違っています。それは、Pythonがこの目的のために__str__
を気にしないことが判明しました。
代わりに、それは別の方法を探します__repr__
、 それが見つからない場合は、それは「16進数の事」にフォールバックします。[2]
だから私は同じことをする2つの方法を作らなければならないと言っているのですか? 1つは自分のカードとそれが何らかのコンテナに入っているときに別のカードをprint
たいときです。
いいえ、最初に、 __str__
と__repr__
両方の__str__
を実装する場合は、クラスがどのようなものになるかを見てみましょう:
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__
、二つの方法を区別することを除いて、正確に同じである、 (S)
によって返される文字列に追加さ__str__
及び(R)
によって返される文字列に付加される__repr__
。
__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)
それらの複製機能について
Pythonの開発者は、同じ文字列をstr()
とrepr()
から返すようにしたい場合、メソッドを機能的に複製する必要があります。
その代わりに、その必要性を排除するための仕組みがあります。私はこの時点まであなたを詮索しています。これは、クラスが実装している場合ことが判明し__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__
実装することが、Pythonのコードレビューを行う際に最初に__repr__
1つであると述べています。単純な方法でデバッグステートメント、クラッシュレポート、またはログファイルに追加できる情報量は、デフォルトで与えられている手ごろな、そしてしばしば役立たない(タイプ、ID)情報と比較すると圧倒されます。
あなたがのために異なる表現をしたい場合は、例えば、容器の内側に、あなたは両方を実装することをお勧めします__repr__
と__str__
メソッドを。 (これらの2つの方法を以下ではどのように使い分けるかについての詳細はこちら)。
両方のメソッドが実装され、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)