Django Python

[Python実践]argparseに挑戦してみよう!《add_subparsers編》

かるでね

こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。

今回はPythonの「argparse」の基礎と「parse_args」と「add_subparsers」の使い方について解説していこうと思います!

第1弾と第2弾はこちら!

argparse」はコマンドライン引数を渡してくれるもので、Pythonエンジニアを目指している方は知っておいた方が良いものになります。

ぜひこの記事でマスターしていってください!

この記事はPythonの公式ドキュメントを参考にしています。

Pythonに限らず、プログラミング言語の公式ドキュメントは非常に読みにくいので、この記事でわかりやすく解説していきます。

argparse使ってみたい

コマンドライン引数を渡してみたい

argparseって何?

自作モジュール作ってみたい

他の人が書いたコードでargparseの部分がわからない

このような人のお役に立てれば幸いです。

それでは早速本題に入っていきましょう!

「argparse」はPythonの基礎を理解していないと難しいので、Pythonの基礎を理解していない人は以下を参考に学習してください!

概要

まずはargparseについて説明していきましょう。

argparseは一言で言えば以下のようになります。

コマンドライン引数を渡せる。

これではよくわからないですよね。

コマンドライン引数とは、以下のようにPythonファイルを呼び出す際に渡せる値のことです。

python main.py --n cardene --age 30

少し詳しく説明すると、--nnameで今回はcardeneを渡しています。

--age30という値を渡しています。

def user_profile(name, age):
    print(name)
    print(age)

こんな感じの関数があったとして、cardenename引数に、30ageに渡されるということです。

どうでしょうか理解できたでしょうか?

このように引数が指定されることで、ユーザーごとにカスタムして使えるようにになります。

僕自身Pypiに自作モジュールをあげたことがあります。

Comnamepy

このモジュールもterminalでも使用することができ、その際にはコマンドライン引数を渡す必要があります。

この記事ではその方法を学んでいきます。

コマンドライン引数を設定する際、結構細かく設定できるのでぜひ全てマスターしていってください!

基本

まずは基本的な使い方をざっくりみていきます。

理解できなくても大丈夫なのでまずはみていきましょう!

まずはArgumentParserオブジェクトを作成することから始める。

import argparse

parser = argparse.ArgumentParser(description='かるでねブログ用のテストコード')

parserというオブジェクトを作成しました。

ここに2つのコマンドライン引数を追加していきます。

import argparse

parser = argparse.ArgumentParser(description='かるでねブログ用のテストコード')

parser.add_argument('name', type=str, nargs='+', default='cardene', help='username')
parser.add_argument('--age', '-a', nargs='+', type=int, help='user age')

args = parser.parse_args()

nameageという引数を追加しました。

terminalで確認してみましょう。

python main.py --h

usage: main.py [-h] [--age AGE [AGE ...]] name [name ...]

かるでねブログ用のテストコード

positional arguments:
  name                  username

optional arguments:
  -h, --help            show this help message and exit
  --age AGE [AGE ...], -a AGE [AGE ...]
                        user age

1行目のコマンドをterminalに入力すると以下のように出力されます。

--hhelpの略で、コマンドライン引数の使い方を確認できます。

positional argumentsは必須に引数です。

optional argumentsオプションの引数で、なくても問題ないものです。

コード上でどう指定するかというと、Pythonコードのname--ageの部分を見ればわかります。

--というのが先頭についているとオプション引数で、ついていないと必須引数となります。

早速コマンドライン引数を渡してみましょう!

terminalで以下のコマンドを実行してください。

python main.py cardene -a 30

namecardeneを渡し、age30を渡しました。

Namespace(name=['cardene'], age=[30])

そうすると上記のように出力されました。

ここまでで基本的な使い方を確認しました!

parse_args

概要

渡された引数と値の解析。

コマンドライン引数とそれに紐づく値を解析して、適切にリストに格納してくれます。

parse_argsにも指定できるいくつかの引数があるので確認しましょう。

ポイント

  • args
    • 解析する文字列のリスト。
  • namespace
    • 属性を代入するオブジェクト。

百聞は一見に如かず」ということで早速1つ1つみていきましょう。

args

「コマンドライン引数」とそれに紐づく値のリスト。

パーサーに渡したいコマンドライン引数とその値の組みをリストにしたものです。

args = parser.parse_args(["cardene", "--age=10"])

このように指定できます。

位置引数は特にコマンドライン引数名を指定せず、オプション引数のみ指定します。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args(["cardene", "--age=10"])

print(args)

8行目で先ほどと同じように指定していますね。

terminalでpython main.py cardene --age 10と実行してください。

Namespace(name='cardene', age=10)

ちゃんと自分が指定した通りに出力されていますね。

NameSpace

新しいオブジェクトの名前。

新しいパーサーオブジェクトの名前のことです。

デフォルトではNameSpaceです。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args()

print(args)

terminalでpython main.py cardene --age 10と実行してください。

Namespace(name='cardene', age=10)

何も指定していないのでNameSpaceと表示されていますね。

では名前を設定してみましょう。

import argparse

class Cardene:
    pass

cardene = Cardene()

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args(namespace=cardene)

print("*"*50)
print(f"args: {args}")
print("*"*50)

print(f"cardene: {cardene}")
print(f"cardene.name: {cardene.name}")
print(f"cardene.age: {cardene.age}")

3行目と4行目でCardeneクラスを定義して、6行目でcardeneインスタンスを作成しています。

13行目で先ほどのcardeneインスタンスを指定しています。

では出力を確認してみましょう。

terminalでpython main.py かるでね --age 10と実行してください。

**************************************************
args: <__main__.Cardene object at 0x10d6a7e80>
**************************************************
cardene: <__main__.Cardene object at 0x10d6a7e80>
cardene.name: かるでね
cardene.age: 10

2行目と4行目はcardeneのオブジェクト情報が出力されています。

5行目でnameに格納されている値が、6行目にageに格納されている値が出力されています。

不正な引数

ここではエラーが起きてしまうようなコマンドライン引数の渡し方についてみていきましょう。

位置引数をオプション引数のように指定

位置引数を指定する際、--をつけずに指定するのが普通です。

では--をつけるとどうなるでしょうか?

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args()

print(args)

terminalでpython main.py --name cardene --age 10と実行してください。

usage: main.py [-h] [--age AGE] name
main.py: error: unrecognized arguments: --name

--nameというコマンドライン引数はないよと言われていますね。

オプション引数を位置引数のように指定

今度は逆にオプション引数--をつけて「位置引数」のように指定してみましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args()

print(args)

terminalでpython main.py cardene age 10と実行してください。

usage: main.py [-h] [--age AGE] name
main.py: error: unrecognized arguments: age 10

ageという位置引数がないよと言われていますね。

用意されていない「コマンドライン引数」を指定

当たり前のことですが定義されていないコマンドライン引数を指定してもエラーが起きるだけです。

一応確認しましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args()

print(args)

terminalでpython main.py cardene --age 10 --height 173と実行してください。

定義されていないheightというコマンドライン引数を指定しています。

usage: main.py [-h] [--age AGE] name
main.py: error: unrecognized arguments: --height 173

予想通りエラーが出ましたね。

マイナスかハイフンかわからない

オプション引数を指定するときに---と指定できます。

ちょっと特殊ですが、-1というオプション引数を定義したとき、値にマイナスが含まれているとエラーが起きます。

実際に見ていきましょう。

まずはエラーが起きない方法から。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')
parser.add_argument('-1', dest='one', type=int, help='user number')

args = parser.parse_args()

print(args)

7行目で-1というオプション引数を指定していますね。

terminalでpython main.py cardene --age 10 -1 100と実行してください。

Namespace(name='cardene', age=10, one=100)

今回は普通に認識してくれました。

では100-100にしてみましょう。

terminalでpython main.py cardene --age 10 -1 -100と実行してください。

usage: main.py [-h] [--age AGE] [-1 ONE] name
main.py: error: argument -1: expected one argument

-100-1部分が値なのかオプション引数なのか認識できなくなっていますね。

短縮指定

コマンドライン引数名を短縮して指定することができます。

これは特に何か定義する必要はなく、デフォルトで使えます。

一応オフにすることもできるので以下を参考にしてください。

ではみていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args()

print(args)

terminalでpython main.py cardene --ag 10と実行してください。

Namespace(name='cardene', age=10)

ちゃんと出力されていますね。

terminalでpython main.py cardene --ag 10と実行してください。

Namespace(name='cardene', age=10)

terminalでpython main.py cardene --a 10と実行してください。

Namespace(name='cardene', age=10)

どちらも出力できましたね。

ただし先頭が一致しないとエラーが起きます。

terminalでpython main.py cardene --ge 10と実行してください。

usage: main.py [-h] [--age AGE] name
main.py: error: unrecognized arguments: --ge 10

短縮する際は先頭が一致しているか確認してください。

動作確認

ここまで動作確認をterminalを通して行ってきました。

ちょっと面倒だったと思うので普通にエディタで実行する方法もみていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('name', help='username')
parser.add_argument('--age', type=int, help='user age')

args = parser.parse_args(['cardene', '--age=10'])

print(args)

8行目で['cardene', '--age=10']と指定しています。

これは以下と同じです。

python main.py cardene --age 10

では実行してみてください。

Namespace(name='cardene', age=10)

ちゃんと実行できましたね。

add_subparsers

「パーサー」を処理ごとに分ける。

異なる機能を持ったプログラムが異なるコマンドライン引数を必要とするときに便利です。

例えば「ユーザーのプロフィール情報」と「ほしい物リスト」という2つの機能があるとき、必要な引数が変わってきます。

その際に、このadd_subparsersを使うことで、パーサー側で渡された値によって処理先を振り分けることができます。

早速みていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('number1', help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!')

parser_add = subparsers.add_parser('add_num', help='add_numだよ!')
parser_add.add_argument('--add', type=int, help='addだよ!')

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')

args = parser.parse_args()

print(args)

急に長くなって驚いたと思いますが怖がらずついてきてください!

6行目で「サブパーサー」を作成しています。

8行目と11行目で、そのサブパーサーadd_numsub_numというパーサを追加しています。

そのパーサーにそれぞれ--add--subというコマンドライン引数を追加しています。

まずはhelpで構造を見ていきましょう。

terminalでpython main.py -hと実行してください!

usage: main.py [-h] number1 {add_num,sub_num} ...

positional arguments:
  number1            number1だよ!
  {add_num,sub_num}  サブパーサーだよ!
    add_num          add_numだよ!
    sub_num          sub_numだよ!

optional arguments:
  -h, --help         show this help message and exit

5行目でサブパーサーの一覧が表示されていますね。

その下の6行目と7行目でサブパーサーの説明が表示されています。

では早速サブパーサーを呼び出してみましょう。

まずはadd_numから呼び出します。

terminalでpython main.py 10 add_num --add 100と実行してください。

Namespace(number1='10', add=100)

add_numだけ呼ばれているのが確認できますね。

では次にsub_numを呼び出します。

terminalでpython main.py 10 sub_num --sub 200と実行してください。

Namespace(number1='10', sub=200)

今度はsub_numだけ呼び出されていますね。

引数

ここでは指定できる引数をみていきましょう。

ポイント

  • title
    • ヘルプ出力でのサブパーサーグループのタイトル。
  • description
    • ヘルプ出力に表示されるサブパーサーグループの説明。
  • action
    • コマンドラインにこの引数があったときの基本のアクション。
  • dest
    • サブコマンド名を格納する属性の名前。
  • required
    • サブコマンドが必須であるかどうかを指定。
  • help
    • ヘルプ出力に表示されるサブパーサーグループのヘルプ。
  • metavar
    • 利用可能なサブコマンドをヘルプ内で表示するための文字列。

ではいくつかみていきましょう。

description

「サブパーサー」グループの説明。

ヘルプ出力時に表示されるサブパーサーグループの説明を設定できます。

デフォルトはNoneになっています。

ではみていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('number1', help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!', description='計算する')

parser_add = subparsers.add_parser('add_num', help='add_numだよ!')
parser_add.add_argument('--add', type=int, help='addだよ!')

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')

args = parser.parse_args()

print(args)

6行目でdescription='計算する'と設定していますね。

では、terminalでpython main.py -hと実行してください。

usage: main.py [-h] number1 {add_num,sub_num} ...

positional arguments:
  number1            number1だよ!

optional arguments:
  -h, --help         show this help message and exit

subcommands:
  計算する

  {add_num,sub_num}  サブパーサーだよ!
    add_num          add_numだよ!
    sub_num          sub_numだよ!

10行目に計算すると出力されていますね。

title

「サブパーサー」グループのタイトル。

サブパーサーグループのタイトルを設定できます。

デフォルトでは前述したdescriptionが指定されているときはsubcommandsと表示され 、指定されていないときは「位置引数」名が表示されます。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('number1', help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!', description='計算する', title="calculation")

parser_add = subparsers.add_parser('add_num', help='add_numだよ!')
parser_add.add_argument('--add', type=int, help='addだよ!')

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')

args = parser.parse_args()

print(args)

6行目でtitle="calculation"と指定していますね。

terminalでpython main.py -hと実行してください。

usage: main.py [-h] number1 {add_num,sub_num} ...

positional arguments:
  number1            number1だよ!

optional arguments:
  -h, --help         show this help message and exit

calculation:
  計算する

  {add_num,sub_num}  サブパーサーだよ!
    add_num          add_numだよ!
    sub_num          sub_numだよ!

9行目でちゃんとcalculationと表示されていますね。

その他

この他にも指定できる引数はいくつかあるのですが、1つ前の記事でそれについて解説も含め紹介しています。

なのでぜひそちらを参考にしてください。

aliases

「サブパーサー」名を短縮。

デフォルトではサブパーサー名を短縮して指定することはできません。

しかし、aliasesを設定することで、このサブパーサー名を短縮した使用することができます。

「コマンドライン引数」は短縮できるので混乱しないようにしてください。

では早速みていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('number1', help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!', description='計算する', title="calculation")

parser_add = subparsers.add_parser('add_num', help='add_numだよ!')
parser_add.add_argument('--add', type=int, help='addだよ!')

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')

args = parser.parse_args()

print(args)

まずはaliasesを設定しないでサブパーサーを短縮指定してみます。

terminalでpython main.py 10 add_nu --add 100と実行してください。

usage: main.py [-h] number1 {add_num,sub_num} ...
main.py: error: argument {add_num,sub_num}: invalid choice: 'add_nu' (choose from 'add_num', 'sub_num')

add_nuなんてサブパーサーはないよと言われていますね。

では短縮指定できるようにしていきましょう。

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('number1', help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!', description='計算する', title="calculation")

parser_add = subparsers.add_parser('add_num', help='add_numだよ!', aliases=['an'])
parser_add.add_argument('--add', type=int, help='addだよ!')

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')

args = parser.parse_args()

print(args)

8行目でaliases=['an']と指定しています。

これはanと指定することで、この「サブパーサー」を指定できるということです。

terminalでpython main.py 10 an --add 100と実行してください。

Namespace(number1='10', add=100)

サブパーサーを短縮指定しましたが、ちゃんと実行できていますね。

サブパーサーごとに関数用意

受け取った値を関数に渡して処理する。

ここまでの説明だと具体的なサブパーサーの使い方がわからないと思います。

なのでこの章では具体的な使い方を解説していきます。

サブパーサーごとに関数を指定することができ、受け取ったコマンドライン引数の値をその関数に渡し処理することができるようになります。

ではみていきましょう。

import argparse

def add_num(args):
    print(f"足し算結果: {args.number1 + args.add}")
    
def sub_num(args):
    print(f"引き算結果: {args.number1 - args.sub}")

parser = argparse.ArgumentParser()

parser.add_argument('number1', type=int, help='number1だよ!')
subparsers = parser.add_subparsers(help='サブパーサーだよ!', description='計算する', title="calculation")

parser_add = subparsers.add_parser('add_num', help='add_numだよ!')
parser_add.add_argument('--add', type=int, help='addだよ!')
parser_add.set_defaults(func=add_num)

parser_sub = subparsers.add_parser('sub_num', help='sub_numだよ!')
parser_sub.add_argument('--sub', type=int, help='subだよ!')
parser_sub.set_defaults(func=sub_num)

args = parser.parse_args()

args.func(args)

3行目と4行目でadd_num関数を定義し、16行目でparser_addパーサーに渡しています。

同様に6行目と7行目でsub_num関数を定義し、20行目でparser_subパーサーに渡しています。

ではまずはadd_numから呼び出してみましょう。

terminalでpython main.py 10 add_num --add 100と実行してください。

足し算結果: 110

add_num関数が呼ばれて、渡された2つの数字を足して返しています。

次にsub_numを呼び出してみましょう。

terminalでpython main.py 10 sub_num --sub 100と実行してください。

引き算結果: -90

今度はsub_num関数が呼ばれて、2つの数字が引き算されているのが確認できます。

以上のように関数を定義して、サブパーサーごとに処理を託すのがよく使われるやり方です。

ぜひ参考にしてください。

その他

他にもさまざまなユーティリティがあります。

しかし、必須ではないのでこの記事での解説は控えます。(長くなってしまうため)

気になる方はこちらから参考にしてください。

最後に

今回は「argparse」の基礎と「parse_args」、「add_subparsers」の使い方について解説してきました。

今回の知識だけでは「argparse」を使いこなすことはできません。

しかし全部解説すると長くなってしまうので別記事で紹介しています。

ぜひそちらも併せて読んでいただければと思います。

普段はPythonやブロックチェーンメインに情報発信をしています。

Twiiterでは図解でわかりやすく解説する投稿をしているのでぜひフォローしてくれると嬉しいです!

-Django, Python
-,