はじめに
デカリボ、パトラッシュ、お誕生日おめでとうございます! nikkieです1
Python標準ライブラリのcsvモジュールとの付き合いが長いのですが、このたびreader(やwriter)のdialect引数の意味をようやく完全理解しました!
ここにアウトプットします。
目次
その出会いは入門者向けハンズオン
csvモジュールには、PyNyumonという入門者向けハンズオン2で出会いました。
pynyumon/2_scraping.md at 9d3a9cfc33b78043ea79bd00d4507eb1abea7402 · pynyumon/pynyumon · GitHub
import csv with open('some.csv', 'r') as f: reader = csv.reader(f) for row in reader: print(row)
CSVファイルを使うときは、だいたいこのコードをベースにします3。
何回も書いているので手が覚えています(親の顔を見るより書いたコードなんじゃないかな)。
それほどまでに慣れたコードではあるのですが、csv.reader(f)
という部分が何をやっているかは、昨日までの私は全く分かっていなかったのです。
csv
モジュールのドキュメント再訪
csv.reader
https://docs.python.org/ja/3/library/csv.html#csv.reader
改めて引数を確認すると、csv.reader(csvfile, dialect='excel', **fmtparams)
となっています。
カンマ区切り(CSV形式)のファイルを読み込むときはcsvfile
引数のみを指定しますね。
タブ区切り(TSV形式)のファイルを読み込むときはdelimiter="\t"
と区切り文字も指定します。
これは、可変長キーワード引数fmtparams
を利用しているということです。
別のオプションである fmtparams キーワード引数は、現在の表現形式における個々の書式パラメータを上書きするために与えることができます。
引用した中に「上書き」とあります。
うわがき?
そう、書式化パラメタのグループが指定されていて、その一部を上書きしているんです(この理解が今回の一番の収穫です)。
書式化パラメタのグループを指定しているのが、dialect
引数!
オプションとして dialect パラメータを与えることができ、特定の CSV 表現形式 (dialect) 特有のパラメータの集合を定義するために使われます。
詳細が書かれた節「Dialect クラスと書式化パラメータ」を見ていきましょう。
Dialect クラスと書式化パラメータ
レコードに対する入出力形式の指定をより簡単にするために、特定の書式化パラメータは表現形式 (dialect) にまとめてグループ化されます。
csv.Dialect
というクラスがあるんです!
https://docs.python.org/ja/3/library/csv.html#csv.Dialect
https://github.com/python/cpython/blob/v3.11.3/Lib/csv.py#L23-L52
class Dialect: # 一部抜粋 delimiter = None quotechar = None escapechar = None doublequote = None skipinitialspace = None lineterminator = None quoting = None def __init__(self): # 省略 def _validate(self): # 省略
属性の意味は「Dialect クラスと書式化パラメータ」節で解説されます。
例えばdelimiter
4は区切り文字ですね。
https://docs.python.org/ja/3/library/csv.html#csv.Dialect.delimiter
フィールド間を分割するのに用いられる 1 文字からなる文字列です。デフォルトでは ',' です。
csv.reader
のdialect
引数のデフォルト値は'excel'
という文字列でした。
これはcsv.excel
クラスを指定していることになります。
https://docs.python.org/ja/3/library/csv.html#csv.excel
excel クラスは Excel で生成される CSV ファイルの通常のプロパティを定義します。これは 'excel' という名前の dialect として登録されています。
具体的な設定値を見たいので、ソースコードも覗きます。
https://github.com/python/cpython/blob/v3.11.3/Lib/csv.py#L54-L62
class excel(Dialect): delimiter = ',' quotechar = '"' doublequote = True skipinitialspace = False lineterminator = '\r\n' quoting = QUOTE_MINIMAL
Dialectの各属性が設定されていますね。
ここにない属性はベースクラスのデフォルト値が使われるわけですね。
これらの属性には不変式があり、_validate
メソッドを備えたクラスとして実装されているんだろうな〜という理解です5。
以下の気付きがありました。
- CSVファイルを読み込むときは
csv.excel
というDialectの指定で十分なので、ほかの引数は指定しない - TSVファイルを読み込むときは
csv.excel
の中のdelimiter
だけ"\t"
に上書きするために指定していた
TSVファイル用のDialectの存在を知る
ドキュメントを眺める中で、csv.excel
のTSV版を知りました。
その名もcsv.excel_tab
!
https://docs.python.org/ja/3/library/csv.html#csv.excel_tab
excel_tab クラスは Excel で生成されるタブ分割ファイルの通常のプロパティを定義します。これは 'excel-tab' という名前の dialect として登録されています。
実装を見てみると、たしかにdelimiter
以外はcsv.excel
と共通です!
https://github.com/python/cpython/blob/v3.11.3/Lib/csv.py#L64-L67
class excel_tab(excel): delimiter = '\t'
これまでTSVファイルを読み込むときは、以下のように書いてきました。
with open("some.tsv", encoding="utf8", newline="") as f: reader = csv.reader(f, delimiter="\t") # readerを処理する
これはdialect引数を指定するだけでも書けるわけですね。
with open("some.tsv", encoding="utf8", newline="") as f: reader = csv.reader(f, dialect="excel-tab") # readerを処理する
タイプ量が短くなるわけではないですが(むしろ増えている)、csvモジュールに用意されていたわけだから、今後はこちらの書き方にしてみようかな
終わりに
csv.reader(f)
が何をやっているか理解したことをアウトプットしました。
- 書式化パラメタのグループ:Dialect
csv.excel
というDialectがデフォルトで指定されている
- 個別の書式化パラメタを上書きできる
- TSVには
delimiter="\t"
と指定するが、これはDialectのdelimiter属性を上書きしている
- TSVには
- TSV用のDialect
csv.excel_tab
が存在する
一度知ってしまうと、あまりに長い間、何も分かっていない状態で使い続けていたな〜と穴掘って埋まってしまいたくなります。
ただ今回知ることができたので、今後はcsvモジュールをこれまでよりも数段理解した状態で使えるのは楽しみですね。
今回紹介した個々の要素は『Python実践レシピ』の13.1でも取り上げられていました。
『Python実践レシピ』はcsvモジュールのドキュメントの前段階としてよさそうです。
またドキュメントを当たる上で、このエントリはDialectにまつわる一つのストーリーを切り出した立ち位置となるかなと思います!
P.S. Dialectを自動で設定できる!
csv.Sniffer
というものの存在も知りました。
https://docs.python.org/ja/3/library/csv.html#csv.Sniffer
csv.Sniffer().sniffメソッドでdialectを推理させて、それをcsv.readerに渡すわけですね(利用例のコード参照)
ドキュメントのコードの解説が『Python実践レシピ』にありました(利用シーンも紹介されています)
-
優子先輩(部長)ももちろんおめでとうございます!
↩🎀𝙃𝙖𝙥𝙥𝙮 𝘽𝙞𝙧𝙩𝙝𝙙𝙖𝙮🎀
— アニメ「響け!ユーフォニアム」公式 (@anime_eupho) 2023年4月15日
本日4月15日は、
北宇治高校吹奏楽部3年生 トランペット担当
吉川優子の誕生日です✨
おめでとうございます🎂#anime_eupho pic.twitter.com/9nVsZLNjha - 同種コレクションの辞書とも出会ったハンズオンです。 いったいいつから異種コレクションとしての辞書が僕の中で自然になってしまったのだろうか - nikkie-ftnextの日記↩
- 組み込み関数openの引数は2箇所変えます。newline引数とencoding引数も指定します。前者については https://docs.python.org/ja/3/library/csv.html#id3 をどうぞ↩
- delimiterは綴りを間違えることが多いのですが、delimit(境界を定める)という動詞があることを知りました↩
- 不変式については 読書ログ | 『ロバストPython』10章「クラス」、不変式を維持せよ!と声高に叫ぶ章(議論する前の理解のまとめ) - nikkie-ftnextの日記 をどうぞ↩