Pythonでクラスのインスタンスを生成して格納した変数を出力すると、class名とメモリ番地みたいなやつが出力されてしまいます。Pythonユーザーで機械学習とかで遊んでいる方はわかると思いますが、numpyはnp.ndarrayというクラスを持ちながら出力では行列が出力されます。線形代数計算ライブラリを自作している際、そのような特殊な出力にしたいことがありました。実際に行った方法をお話しします。
正直タイトルにあるように、出落ちなのですが......
*__str__*というPython独自の特殊メソッドを用いて、やりたいことを実現していきたいと思います。
やりたいこと
まずは、実際にやりたいことを明確にしていきます。numpyのような物を参考にしますが、今回は簡単のため、Scalarクラスを作ります。
このクラスは、数値(intもしくはfloat)を受け取って、それをdataとして格納させます。
求める最終形はこちらです。
>>> scalar = Scalar(5)
>>> print(scalar)
Scalar(5)
本来であれば、*<__main__.Scalar object at 0x7f85f3a00150>*のように表されてしまいますが、*Scalar(5)*というような特殊文字列にします。
Scalarクラスを作る
さて、これからScalarクラスを作ります。
初めは、最低限クラスとして機能するようなコードを書いていきます。
Scalarクラスの仕様は以下とします。
- インスタンス生成時にint又はfloatを受け取る
- 上の条件以外ならば、TypeErrorを発出
これらの条件を満たすようにクラスを書いたのが以下になります。
class Scalar:
def __init__(self, data):
if isinstance(data, int) or isinstance(data, float):
self.data = data
else:
raise TypeError('expect int or float, but {}', type(data))
これでメインルーティンを書いていきます。
以下を試してみます。
- intを入力
- floatを入力
- listを入力
さて、書きたします。先ほどのクラスを書いたファイルを拡張していきます。
class Scalar:
def __init__(self, data):
if isinstance(data, int) or isinstance(data, float):
self.data = data
else:
raise TypeError('expect int or float, but {}', type(data))
if __name__ == "__main__":
scalar = Scalar(3)
print(scalar)
scalar = Scalar(3.0)
print(scalar)
scalar = Scalar([3])
print(scalar)
ターミナルで実行してみます。
$ python scalar.py
<__main__.Scalar object at 0x7fddd4300150>
<__main__.Scalar object at 0x7fddd4300190>
Traceback (most recent call last):
File "scalar.py", line 13, in <module>
scalar = Scalar([3])
File "scalar.py", line 6, in __init__
raise TypeError('expect int or float, but {}', type(data))
TypeError: ('expect int or float, but {}', class 'list')
3と3.0の入力は受け取れましたが、どうやら[3]の入力は期待した通りエラーを出してくれています。
しかし、やはりこのままではクラスをそのまま出力してしまっています。
【str】の出番
それでは、__str__という特殊メソッドを用いて、出力結果を変更していきます。
先までのクラスを以下のように変えていきます。
class Scalar:
def __init__(self, data):
if isinstance(data, int) or isinstance(data, float):
self.data = data
else:
raise TypeError('expect int or float, but {}', type(data))
<span class="comment"># 以下を追加
def __str__(self):
return 'Scalar({})'.format(self.data)
最後の2行を追加する感じです。
それでは、intとfloat以外はエラーを出すことを確認しているのでメインルーティンも同時に変えて以下のファイルとして完成させましょう。
class Scalar:
def __init__(self, data):
if isinstance(data, int) or isinstance(data, float):
self.data = data
else:
raise TypeError('expect int or float, but {}', type(data))
def __str__(self):
return 'Scalar({})'.format(self.data)
if __name__ == "__main__":
scalar = Scalar(3)
print(scalar)
scalar = Scalar(3.0)
print(scalar)
この出力結果はどうなるでしょうか......(ドキドキ??)
$ python scalar.py
Scalar(3)
Scalar(3.0)
期待した通りの結果ですね!!
このように、クラスのインスタンスの出力を制御するには**str**という特殊メソッドを使うと良いみたいです。
ただし、__reprというものもあります。今回のソースに関しては正直いうと、この**repr**の方が適している気がします。(evalを使えば可逆的に戻すことができるため)
詳しくはこちらを参照されたい(https://docs.python.org/ja/3/reference/datamodel.html)
今回のソースの推奨
class Scalar:
def __init__(self, data):
if isinstance(data, int) or isinstance(data, float):
self.data = data
else:
raise TypeError('expect int or float, but {}', type(data))
def __repr__(self):
return 'Scalar({})'.format(self.data)
if __name__ == "__main__":
scalar = Scalar(3)
print(scalar)
scalar = Scalar(3.0)
print(scalar)