argparseの用法まとめ

## argparseの基本トピック
### argparseの概要
`argparse`は`Python`スクリプトを実行する際に様々なオプションで実行できるようにコマンドラインから引数を与えるためのライブラリです。

```python:argparse1.py
import argparse
# https://docs.python.org/ja/3.13/library/argparse.html

parser = argparse.ArgumentParser()
parser.add_argument('--filename', help="filename")
args = parser.parse_args()

print(args)
print(args.filename)
```

たとえば上記のようなスクリプトを`argparse1.py`に保存した場合、下記のように実行時の引数によってプログラムの出力が変わります。

・コマンド1
```
$ python argparse1.py
```

・実行結果1
```
Namespace(filename=None)
None
```

・コマンド2
```
$ python argparse1.py --filename test.txt
```

・実行結果2
```
Namespace(filename='test.txt')
test.txt
```

・コマンド3
```
$ python argparse1.py --filename argparse1.py
```

・実行結果3
```
Namespace(filename='argparse1.py')
argparse1.py
```


### argparseの引数の型
DeepLearningの実装を行う場合に`argparse`に引数を追加して実行する場合などは多いと思います。このような際にエラーが生じやすいのが`argparse`の引数の型です。デフォルトでは引数が`str`型なので数字を取り扱う際は下記のように`type=int`や`type=float`を用いる必要があります。

```python:argparse2.py
import argparse


parser = argparse.ArgumentParser()
parser.add_argument('--filename', help="filename")
parser.add_argument('--num_epoch1')
parser.add_argument('--num_epoch2', type=int)
parser.add_argument('--learning_rate1')
parser.add_argument('--learning_rate2', type=float)
args = parser.parse_args()

print(args)
print(type(args.filename))
print(type(args.num_epoch1))
print(type(args.num_epoch2))
print(type(args.learning_rate1))
print(type(args.learning_rate2))
```

・コマンド1
```
$ python argparse2.py
```

・実行結果1
```
Namespace(filename=None, num_epoch1=None, num_epoch2=None, learning_rate1=None, learning_rate2=None)
<class 'NoneType'>
<class 'NoneType'>
<class 'NoneType'>
<class 'NoneType'>
<class 'NoneType'>
```

・コマンド2
```
$ python argparse2.py --filename test.txt --num_epoch1 10 --num_epoch2 10 --learning_rate1 0.1 --learning_rate2 0.1
```

・実行結果2
```
Namespace(filename='test.txt', num_epoch1='10', num_epoch2=10, learning_rate1='0.1', learning_rate2=0.1)
<class 'str'>
<class 'str'>
<class 'int'>
<class 'str'>
<class 'float'>
```

実行結果より、`parser.add_argument`の引数に`type=int`や`type=float`をしない場合は引数が`str`型で取り扱われることが確認できます。`type`の指定はつい抜けがちなので注意しておくとよいと思います。


### 引数のデフォルト値の設定
`argparse`でコマンドラインから引数を与えるにあたって、毎回同じ値を指定する場合は手間が発生します。この解決にあたっては、`add_argument`に`default`値を予め記載しておくと良いです。

```python:argparse3.py
import argparse


parser = argparse.ArgumentParser()
parser.add_argument('--filename', help="filename")
parser.add_argument('--num_epoch', type=int, default=5)
parser.add_argument('--learning_rate', type=float, default=0.01)
args = parser.parse_args()

print(args.filename)
print(args.num_epoch)
print(args.learning_rate)
```

・コマンド1
```
$ python argparse3.py
```

・実行結果1
```
None
5
0.01
```

・コマンド2
```
$ python argparse3.py --filename test.txt --num_epoch 10 --learning_rate 0.1
```

・実行結果2
```
test.txt
10
0.1
```

実行結果より、`default`によってデフォルト値を指定しておくことで、引数の指定を省略して良いことが確認できます。


### 入力値の限定
`argparse`でコマンドラインから引数を与えるにあたって、DeepLearningのネットワーク構造のように入力値を限定する必要がある場合などがあります。このような場合は`choices`を用いると良いです。

```python:argparse4.py
import argparse


parser = argparse.ArgumentParser()
parser.add_argument('--filename', help='filename')
parser.add_argument('--num_epoch', type=int, default=5)
parser.add_argument('--net', default='ResNet18', choices=['VGG16', 'ResNet18'])
args = parser.parse_args()


print(args.filename)
print(args.num_epoch)
print(args.net)
```

・コマンド1
```
$ python argparse4.py --filename test.txt
```

・実行結果1
```
test.txt
5
ResNet18
```

・コマンド2
```
$ python argparse4.py --filename test.txt --net VGG16
```

・実行結果2
```
test.txt
5
VGG16
```

・コマンド3
```
$ python argparse4.py --filename test.txt --net VGG16
```

・実行結果3
```
usage: argparse4.py [-h] [--filename FILENAME] [--num_epoch NUM_EPOCH]
                    [--net {VGG16,ResNet18}]
argparse4.py: error: argument --net: invalid choice: 'VGG19' (choose from 'VGG16', 'ResNet18')
```


### 複数の入力の受け取り
`argparse`で複数の入力を受け取る場合は`nargs`に`+`などを指定すると良いです。

```python:argparse5.py
import argparse


parser = argparse.ArgumentParser()
parser.add_argument('--filename', help='filename', nargs='+')
parser.add_argument('--num_epoch', type=int, default=5)
args = parser.parse_args()


print(args.filename)
print(args.num_epoch)
```

・コマンド1
```
$ python argparse5.py --filename test1.txt test2.txt
```

・実行結果1
```
['test1.txt', 'test2.txt']
5
```

・コマンド2
```
$ python argparse5.py --filename test1.txt
```

・実行結果2
```
['test1.txt']
5
```

実行結果を確認すると複数の入力がリスト形式で取り扱われることが確認できます。