Otsuhachi’s diary

プログラミングのことや覚書などその他いろいろ

当ブログの内容を使用したり参考にした場合に生じた問題の責任は負いかねます。

Python組み込み関数4 -ascii・repr・str編-

Pythonおさらい第4弾
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はascii関数repr関数、さらにstr関数について。
知ってる関数で似通ったものはできる限りまとめていきたい。

普段使うのはreprstrで、asciiについてはよく知らない。

読み込むドキュメントはこちら[ascii, repr, str]

構成

今回の関数は以下のようになっている。

ascii(object)

repr(object)

str(object='')

str(object=b'', encoding='utf-8', errors='strict')

ascii

ドキュメントによると、asciireprと同様にオブジェクトの印字可能な表現を含む文字列を返すが、非ASCII文字はエスケープされるらしい。
Python2reprと等価。

repr

reprはオブジェクトの印字可能な表現を含む文字列を返し、その文字列は主に以下のようなものになるらしい。
また、自作クラスでは__repr__()を定義することでこの関数で返す文字列を制御することができる。*2

  1. eval関数に渡されたときと同じ値を持つようなオブジェクト表す文字列
  2. 山括弧に囲まれたオブジェクトの型の名前と追加の情報*3

str

strobjectのみを受け取るものとキーワード引数encoding, errorsを指定するものが存在しているらしい。
objectのみのものは、objectの文字列版を返すが、objectが空の場合は空文字が返る。
自作クラスの場合では__str__()を定義することでこの関数で返す文字列を制御することができる。*4
__str__()を持たないクラスはreprの結果と等価。

キーワード引数encoding, errorsの少なくとも一方が与えられた場合のstrについての説明は現時点ではよくわからないが、objectbyte-like objectである必要があり、bytes.decode(encoding, errors)と等価になるらしい。
今回のおさらいでは割愛。

使ってみる

以下の説明のそれぞれ試すとはascii, repr, strでの出力を試みることである。

  1. __repr__()__str__()も定義しない自作クラスPersonでそれぞれ試す
  2. Personを継承し、__repr__()を定義したRPersonでそれぞれ試す
  3. Personを継承し、__str__()を定義したSPersonでそれぞれ試す
  4. RPerson, SPersonを継承したRSPersonでそれぞれ試す

実行するコードは以下の通り。

from typing import Iterable


class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age


class RPerson(Person):
    def __repr__(self) -> str:
        me = f'{self.name}({self.age})'
        return me


class SPerson(Person):
    def __str__(self) -> str:
        me = f'{self.name}、{self.age}歳です!'
        return me


class RSPerson(RPerson, SPerson):
    pass


def show_person(persons: Iterable):
    for i, person in enumerate(persons):
        if i:
            print()
        print(type(person).__name__)
        print('-' * 55)
        print(f'repr: {repr(person)}')
        print(f'ascii: {ascii(person)}')
        print(f'str: {str(person)}')


me = {'name': 'Otsuhachi', 'age': 28}
persons = [p(**me) for p in (Person, RPerson, SPerson, RSPerson)]
show_person(persons)
Person
-------------------------------------------------------
repr: <__main__.Person object at 0x0000028225D32A60>
ascii: <__main__.Person object at 0x0000028225D32A60>
str: <__main__.Person object at 0x0000028225D32A60>

RPerson
-------------------------------------------------------
repr: Otsuhachi(28)
ascii: Otsuhachi(28)
str: Otsuhachi(28)

SPerson
-------------------------------------------------------
repr: <__main__.SPerson object at 0x0000028225D32820>
ascii: <__main__.SPerson object at 0x0000028225D32820>
str: Otsuhachi、28歳です!

RSPerson
-------------------------------------------------------
repr: Otsuhachi(28)
ascii: Otsuhachi(28)
str: Otsuhachi、28歳です!

表にまとめると以下のようになる。

関数 未定義 reprのみ strのみ 両方
repr <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28)
ascii <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28)
str <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) Otsuhachi、28歳です! Otsuhachi、28歳です!

__repr__()__str__()も定義していない場合に出力される文字列はすべてのクラスのスーパークラスであるobject__repr__()である。
__str__()が定義されていない場合、__repr__()の結果が返るが、その逆は発生しないことがわかる。
asciiについては残念ながらこの例では出力の差異を見ることができなかった。

締め

今回、str関数に2種類あることがわかったが、現状ではasciiのありがたい使い道についてはわからなかった。
__repr__()evel関数で等価なオブジェクトを生成できるものが好ましいという点も記憶の片隅にとどめておいた方がいいかもしれない。
eval関数はセキュリティリスクが面倒くさそうだからそもそもあまり使いたくないイメージ。

__str__()は人にやさしい出力を定義する慣習があるっぽい。
リストの中身が直接見れないからって__repr__()__str__()代わりに使ってる身としては耳が痛い。

*1:わかりやすく解説している他サイトを見ない

*2:第一弾で使用

*3:大抵の場合はオブジェクトの名前とアドレスを含む

*4:第一弾で使用