どくおぷと!

おはようございます、こんにちは、こんばんは。 久々の更新です。

さて、皆さんはpythonスクリプトを書くときコマンドライン引数のパースはどのようにしていますか?

  1. sys.argv[1:]を自力でパース
    コマンドが簡単で、特に使いまわす予定のないスクリプトならそれでもいいですが、自分以外の人がスクリプトを使う場合はこのやり方は嫌われますね。

  2. argparseやgetopt,optparseモジュールを使う
    なるほど既存のモジュールを使うのは賢い選択です。このやり方ならスクリプトの引数を間違えた場合にusageを表示させるのも楽でしょう
    *getopt,optparseモジュールは推奨されていないらしいです。
    argparseでコマンドライン引数の解析 | Python Snippets

ちなみに僕はJupyter NotebookやVSCodeの上でスクリプトを実行することが多かったので今まで2の存在をしらなかったです(恥ずかしい)
ですが先日コマンドライン引数をパースする機会に恵まれまして、その辺のモジュールを調べていたらよりも簡単にパースしてくれるモジュールがありました。 Pythonのコマンドライン引数処理の決定版 docopt (あとJuliaに移植したよ) - りんごがでている

その名も docopt

docoptの詳しい説明は公式をどうぞ

docopt—language for description of command-line interfaces

docoptの画期的なところは__doc__スクリプトの使用方法を書いておけば、それをもとにパースしてくれるという点です。 なので使い方も超簡単です!
基本的には以下の2stepでパースできます
1. スクリプト__doc__を書く
2. argv = docopt(__doc__)

これだけです。

1. スクリプト__doc__を書く
2. argv = docopt(__doc__)

だけです

あんまり簡単なのでつい2回書いてしまいました。それにしても簡単だ。

もう少し説明をするとdocopt()の第一引数はスクリプトの使用方法を記述した文字列、第二引数にコマンドライン引数のリストargvを渡すことができます。第二引数を指定しない場合sys.argv[1:]が使用されます。
docopt()には他にもhelp,version,options_firstといった名前付き引数があるみたいですが、この辺の使い方はおいおい調べます。

そしてdocoptが正常にコマンドをパースできた場合、受け取ったコマンドにキーワードが含まれるか否か表す辞書を返します。

具体的な例で説明すると。(以下公式より抜粋)

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored|--drifting]
  naval_fate.py -h | --help
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.
"""

このような__doc__がかかれたスクリプトnaval_fase.pyがあった時コマンドプロンプトから
"python naval_fate.py ship new Prinz"と入力するとdocoptはsys.argv[1:](すなわちship new Prinz)を解析して,

{
  "--drifting": false, 
  "--help": false, 
  "--moored": false, 
  "--speed": "10", 
  "--version": false, 
  "<name>": [
    "Prinz"
  ], 
  "<x>": null, 
  "<y>": null, 
  "mine": false, 
  "move": false, 
  "new": true, 
  "remove": false, 
  "set": false, 
  "ship": true, 
  "shoot": false
}

という辞書を返します。(公式サイトのTry docopt in your browserに行くとお試しできるよ!)

一行目にはスクリプトの概要を記述します。usageに具体的な文法、optionsにオプション引数を記述します.
usageの各行は <スクリプト名> <スペース区切りのコマンドキーワード> という形式になっています.
上の例に合わせて<スクリプト名>と表記しましたがこの部分は各コマンドで共通であれば実はなんでもよいみたいです.
たとえば

Usage:
  _ ship new <name>...
  _ ship <name> move <x> <y> [--speed=<kn>]
  _ ship shoot <x> <y>
  _ mine (set|remove) <x> <y> [--moored|--drifting]
  _ -h | --help
  _ --version

このようにスクリプトに関係ない記号(_)でもエラーは出ません。 これはdocoptメソッドがデフォルトではsys.argv[1:]をパースするため,もともとスクリプト名がパースされているからと思われます。 プログラム内部でスクリプトを実行する場合なんかに使えるかもですね。