にわかプログラマーにけやのお砂場

主に教科書的なプログラミングについて書きます。たまに趣味とか。

Python3の文字列表現について

そもそも文字列表現とは?

おそらくプログラマーを名乗る人物のほとんどが様々な言語に一度は触れたことだろう。
そして大きく異なるであろう部分の一つが文字列の書き方だと思う。

例えばCでは「文字列」は必ず「"」(ダブルクォーテーション)で囲まなければならないが、
多くの言語では「'」(シングルクォーテーション)と「"」どちらを使っても良いなんてのがわかりやすいだろう。

そして個人的に面白い(そしてややこしい)文字列の表現をするのがPHPPythonである。
PHPの文字列表現については割愛するが、他の言語であまり聞いたことのない仕様であるため機会があればそのことについてでも書こうと思う。

Python2とPython3の文字列

さて文字列表現そのものに触れる前にまずPythonのバージョン違いによる文字関係のクラスに触れたいと思う。

Pythonは2と3で大きく仕様が異なる(後方互換性のない)言語であるが、文字列にも大きなメスが入った。

Python2では文字列の表現方法としてstrクラスとunicodeクラスが存在する。これはunicodeクラスがマルチバイト文字対応文字列、strクラスがマルチバイト文字非対応の文字列と思えばいいだろう。
この仕様は後々Pythonがメジャー化されるにつれて(特に英語に弱い日本で顕著だったのだが)バグの温床の一つであった。
(モジュール内部の処理がstrオブジェクトのみを想定していることが多かった。またアルファベットはasciiコードで基本的に処理可能なため主にマルチバイト文字を処理する環境での開発者が少なかった頃のPythonはこの形式で本当に問題なかったのだろう。)

Python3では文字列を扱うオブジェクトがbyteオブジェクトとstrオブジェクトに変わり、Cに近い環境になったと言える。
byteクラスはバイトコードを扱い、strクラスは文字列をそのまま操作できると、より直感的なプログラムが可能になったように思う。
もっともbyteクラスを直接扱うことはPythonでは比較的少ないとは思われるが。(私は昔ファイルのコピープログラムをPythonでやったりするということで使ったりはした)

Pythonの「文字列表現」

さて、Pythonでは文字列を表現する際幾つかの記法が存在する。 - 通常の文字列 - raw文字列 - format文字列 - byte文字列 - unicode文字列 Python3では全部の記法が使える(unicode文字列は互換性のために残っているものの通常の文字列として扱われる)が、Python2ではformat文字列とbyte文字列は使えない。さらに言うとformat文字列は比較的新しくPython3.6以降でないと使えない。

通常の文字列

どのプログラミング言語でも初めて触れる時には様式美としてHello, World!を出力する(と、少なくとも私は思ってる)。これはPythonの通常の文字列が何かを説明するのにもいいかと思うのでそのまま使ってみよう。

print("Hello, World!")
print("\tTest for escape-sequence")
print('Hello, World!')
print('\tTest for escape-sequence')

実際に実行してみたらわかると思うが、Pythonでは「'」と「"」に違いはない。
どちらの記号を使ってもエスケープシークエンスは正常に動くし、文字列が出力できないなんてこともない。
この記法を用いるとPython2もPython3もともにstrクラスのオブジェクトとして扱われるようになる。
クラスの確認にはtype("確認したいオブジェクト")で十分だろう。
(Pythonでは非推奨のやり方だが今回重要なのはクラスの取得だけなのでそれならばこちらの方が理に適っている。クラスによって挙動を変えたい時はisinstance("文字列", str)みたいにすればいい)

この仕様がPython2時代では厄介だった。まだunicodeクラスを知らないPython初学者が日本語を出力しようとして「通常の文字列で」日本語を記述してしまうケースがとても多かったのだ。文字列だけなら環境によっては出力可能だが、このあと変数や標準入出力関数でエラーが起こりやすい。

raw文字列

raw文字列は文字列をraw記法で記述する方法だ。
文字列の前にrを書くことでこの文字列としてみなされる。
まずは下のプログラムを見て欲しい。

print("\t通常の文字列\n")
print(r"\traw文字列\n")

はてなブログさんがすでにシンタックスで教えてくれてはいるが、エスケープシークエンスが働いていない。
raw(生)の示す通り文字列内の特殊文字や変数などを展開せずそのまま文字列にしてくれるのがraw文字列である。

この記法は最近はやりの機械学習、とくに自然言語処理分野で活躍している(ような気がする)。
それ以外にもreモジュールなど、正規表現を用いる場所では「\」(バックスラッシュ)を多用するため、いちいちエスケープを行わないようにraw文字列で記述することが多い。
使い所が限定されるせいかあまり紹介・活用されていないような気がする少々不憫な文字列とも言える。
(自然言語処理のプログラミングについて解説しているようなところでもraw文字列を使っていないところは結構見受けられる印象)

format文字列

文字列の前にfを追加することでこのformat文字列になる。
Python3.6から追加された新顔、format文字列だが、Pythonにはすでに文字列のformatを行う関数が幾つも存在している。
strクラスのformatメソッドやTemplate()、%記法については別のブログ記事を書くことにしてここでは単純な紹介をだけしよう。
(他人のブログに誘導してもいいのだが自分のブログのモチベのためにあえてそうしないことにした)

name = "にけや"
print(f"私の名前は{name}です。")

このように書くと「私の名前はにけやです。」って出力される。
format文字列にも変数の展開だけでなくformatメソッド以上にformatの種類があるのだがそれも割愛。
PHPを書いていた人はこの変数展開の仕方に見覚えがあるかもしれない。

format文字列はraw文字列と組み合わせて書くことができる。
その関係上なのかformat文字列の展開部分では「\」を使った変数、関数等は使用できない。

byte文字列

Python3のみにある(byteクラスがあるのはPython3だけなので当然なのだが)byte文字列だが、raw文字列以上に使いどころのない文字列だと思われる。
Pythonでバイトに触れる機会がそもそも多くなく、正直それならCやアセンブリコードを書いた方がいいような場面も多いような気がするが紹介だけ。
なお、この文字列はbを文字列の前に置くことが記法になっている。

print(b"Hello, World!")
print(b"Hello, World!".decode())

byte文字列をそのまま出力した場合、bというプレフィクスと「'」で囲まれた文字列が返ってくる。
decodeメソッドはそれを適した文字コードで解釈しstrクラスに戻すメソッドである。
おそらくbyte文字列を使うことはほとんどなくstrクラスからbyteクラスへの変換はstr.encode()を用いるだろう。

byte文字列はraw文字列との併用が可能である。
format文字列とは使えないが、用途的には全く問題ないだろう。

unicode文字列

unicode文字列はPython2のみに存在する文字列で、日本語を母語とする多くのPython2プログラマーは最初にこの記法を覚えざるを得なかったであろう記法である。
文字列の前にuを記述することでこの記法になる。

なお、この記法自体はPython3でも使えるのだが、意味はないものとなっている。
Python2を切り捨てたはずのPython3でこの記法が使えるのはascii文字以外の文字が入っていることをPythonプログラマーに伝えるためのサイン代わりなのだろうか?(調査不足、何か知っている人がいたらコメントしてくれるとありがたい)

print(u"Hello, World!")

Python2での記法であるため上記3種類の文字列との併用は不可。

さいごに

ここまで駆け足の割には長文になったブログ記事を読んでくれた方には是非とも公式のドキュメントだけでなくPEPの方にも触れてみるといいと思う。
最先端の開発状況が確認できるしこれまでのPythonの変遷を知るのにも役立つであろう。