Python組み込み関数1 -print編-
特にネタもないのでよく使う型や関数、ライブラリなんかを自分なりにまとめてみる。
一応制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。
今回はHello World
でも使用するprint関数
について。
正直、キーワードなし引数任意個をsep
で指定した字句で区切って出力できて、end
で末尾の字句を指定できるくらいしか知らない。
ネタついでに勉強になることに期待。
読み込むドキュメントはこちら
構成
何気なく使っているprint関数
は、詳細にはこんな感じらしい。
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
ドキュメントを読む限りでも基本的に上記解釈で問題なさそう。
ネタとしてあんまりなのでもう少し水増ししてみる。
使ってみる
- 自作クラス
Person
に__str__()
を定義して出力 - 自作クラス
Person
をリストmember
に入れ、member
を出力
実行するコードは以下の通り。
class Person: def __init__(self, name: str, age: int): self.name = name self.age = age def __str__(self) -> str: me = f'{self.name}、{self.age}歳です!' return me me = Person('Otsuhachi', 28) member = [me] print(me) print(member)
Otsuhachi、28歳です! [<__main__.Person object at 0x0000023C5175FCA0>]
自作クラスを出力するときは__str__()
が呼び出されているのがわかる。
一方で、リストの中の自作クラスは人が見てもよくわからない出力がされている。
つぎに以下のことを試す。
Person
を継承したPerson2
に__repr__()
を定義して出力Person2
をリストmember
に入れ、member
を出力
実行するコードは以下の通り((Person
クラスは前のコードを再利用する))。
class Person2(Person): def __repr__(self) -> str: me = f'{self.name}({self.age})' return me me = Person2('Otsuhachi', 28) member = [me] print(me) print(member)
Otsuhachi、28歳です! [Otsuhachi(28)]
コンテナオブジェクト内のクラスを出力するときは__repr__()
が呼び出されているのがわかる。
キーワード引数file
出力先をfile
引数で指定できるそうなので、そのあたりについていくつか試す。
Path
個人的にかなり使用頻度の高いライブラリ。
Windows
, Linux
, Mac
問わずファイルなどのパスを扱いやすくしてくれる……らしいがWindows以外を意識したことがないのでそこはなんとも。
実行するためのコードは以下の通り。
from pathlib import Path file = Path('sample_output.txt') print('Hello', 'World', sep='\t', file=file)
Linter
のあるコーディング環境だとすでにエラーの波線が出ている。
ダメ元で実行してみると……。
Traceback (most recent call last): File "~\sandbox.py", line 4, in <module> print('Hello', 'World', sep='\t', file=file) AttributeError: 'WindowsPath' object has no attribute 'write'
ドキュメントに書いていた通り、write
がないということでAttributeError
が発生した。
TextIOWrapper
型名よりもopen(file)
したら勝手に生成されてるといった方が伝わりそうな型。
この型はwrite(str)
できるので、無事出力される……ハズ。
コードはさっきのを少しいじって以下の通り。
from pathlib import Path file = Path('sample_output.txt') with open(file, 'w', encoding='utf-8') as f: print('Hello', 'World', sep='\t', file=f)
正常に出力されていれば、コードを実行したフォルダにsample_output.txt
というファイルができ、タブ区切りでobjects
が出力されている。
Hello World
きちんと出力することができた。
自作クラス
write(str)
できる自作のクラスを定義してみる。
出力先にGUIでメッセージを出したりするのは面倒なので、標準出力に文字を追加するような処理にする。
コードは以下の通り。
class Person: def __init__(self, name: str, age: int): self.name = name self.age = age def write(self, text: str): t = f'{self.name}({self.age}): ' print(t + text) me = Person('Otsu8', 28) print('Hello', 'World', file=me)
個人的には期待を裏切られる結果になったので、結果を予測してから進んでみてほしい。
ちなみに予想はOtsu8(28): Hello World
だった。
Otsu8(28): Hello Otsu8(28): Otsu8(28): World Otsu8(28):
予想通りだっただろうか?
流れ的には'{sep}'.join(objects)+end
のように出力文字列を整形してからme.write
に渡すと思っていたが、実際には以下の順にme.write
に渡されていた模様。
objects[1]
((この場合はHello
))- sep((この場合は
))
objects[2]
((この場合はWorld
))- end((この場合は
\t
))
なぜか?
冷静に考えてみれば、最終出力内容がメモリに収まりきるかどうかなど、予測できないからであると思われる。
基本1, 2文くらいしか同時に出力しないので失念していた。*2
自作クラスで試して得た学び
今までファイルに複数行の出力をしたいとき、大体以下の2通りを使っていた。
- 行
lines
を'\n'.join(lines)
をTextIOWrapper.write
に渡す - 行
lines
をfor文
を使って2行目以降に改行を挿入しながらTextIOWrapper.write
に渡す
上記方法にそこまで大きな不満があったわけではないが、以下のように書くことでスマートに記述できそうである。
print(*lines, sep='\n', end='', file=<TextIOWrapper>)
from pathlib import Path lines = ['Hello', 'World', 'Pythonのprint文', 'おさらいしてよかった!'] file = Path('sample_output.txt') with open(file, 'w', encoding='utf-8') as f: # 1.の方法 # これでも一見良さそうだが、メモリ不足が考慮されていない # f.write('\n'.join(lines)) # 2.の方法 # メモリ不足は心配なさそうだが記述が多い # for i, line in enumerate(lines): # if i: # line = '\n' + line # f.write(line) # 今回の学びで得た方法 print(*lines, sep='\n', end='', file=f)
出力結果はどの方法でも以下の通り。
ファイル末尾に空行がほしい場合も引数end
を指定しないだけでよいのがGood
Hello World Pythonのprint文 おさらいしてよかった!
締め
常識だったのかもしれないが、今回の発見は大きな収穫だった。
いつか同じくドキュメントを読み込まない誰かが見て、驚いてくれればうれしい。
flush
についてはバッファ化などよくわかってない部分の理解も必要そうなので、今回は保留とする。
ドキュメントを読まず、なんとなくで使用している他の機能についてもこのブログを通して発見していけたらと思う。
2時間位掛けての本記事……続けたいが気力的に続くかどうか。
今回のprint関数
が思いのほかアタリだったので、次回以降のネタに満足できるかどうか……。
頑張らない範囲で頑張ろうと思う。