Python / argparse
===
###### tags: `Python`
###### tags: `Python`, `ArgumentParser`, `argument_parser`, `add_argument`, `parse_args`, `store`, `store_true`, `store_false`, `store_const`, `append`, `append_const`, `count`, `version`
<br>
[TOC]
<br>
- [Argparse with two values for one argument](https://stackoverflow.com/questions/34988908/)
```python!
parser.add_argument(
'-c',
'--compare',
nargs=2,
metavar=('newfile', 'oldfile'),
help='Compares previous run results in oldfile with current run results in newfile.',
)
args = parser.parse_args()
newfile, oldfile = args.compare
```

- [What is the right way to treat Python argparse.Namespace() as a dictionary?](https://stackoverflow.com/questions/16878315/)
<br>
## 官方教學
> [[官網] argparse --- 命令行选项、参数和子命令解析器](https://docs.python.org/zh-tw/3/library/argparse.html#)
### nargs
> https://docs.python.org/zh-cn/3.7/library/argparse.html#nargs
> #不定長度 不定個數
- `nargs='?'`
None 或 單一值
- `nargs='+'`
清單 (type=list)
- ### case 1
```
from argparse import ArgumentParser
parser = ArgumentParser()
parser.description = 'Check the FastQC report, and run the Trimmomatic process if necessary.'
parser.add_argument('read1', help='input the R1 of paired-end FASTQ files, or a single-end FASTQ file')
parser.add_argument('read2', nargs='?', help='input the R2 of paired-end FASTQ files')
args = parser.parse_args()
```
執行方式
```
$ python qc_runner.py NA24631_1.fastq.gz
$ python qc_runner.py NA24631_1.fastq.gz NA24631_2.fastq.gz
```
<br>
## 教學
- ### [Python 超好用標準函式庫 argparse](https://dboyliao.medium.com/4eab2e9dcc69)
```python=
# example.py
from argparse import ArgumentParser
parser1 = ArgumentParser()
parser2 = ArgumentParser(prog="my_example")
parser3 = ArgumentParser(usage="usage")
parser4 = ArgumentParser(description="a simple demo of argparse")
parser5 = ArgumentParser(epilog="see the doc: https://docs.python.org/3/library/argparse.html")
parser1.print_help()
# usage: example.py [-h]
#
# optional arguments:
# -h, --help show this help message and exit
parser2.print_help()
# usage: my_example [-h]
#
# optional arguments:
# -h, --help show this help message and exit
parser3.print_help()
# usage: usage
#
# optional arguments:
# -h, --help show this help message and exit
parser4.print_help()
# usage: example.py [-h]
#
# a simple demo of argparse
#
# optional arguments:
# -h, --help show this help message and exit
parser5.print_help()
# usage: example.py [-h]
#
# optional arguments:
# -h, --help show this help message and exit
#
# see the doc: https://docs.python.org/3/library/argparse.html
```
- ### [Python argparse 教學:比 sys.argv 更好用,讓命令列引數整潔又有序](https://haosquare.com/python-argparse/)
<br>
## code snippet
### language: str
```python=
parser.add_argument(
'-l', '--lang', type=str, choices=['en', 'tw'],
default=os.getenv('LANG', 'en')[:2], # LANG=en_US.UTF-8 -> LANG=en
help='The language to use: "en" for English, "tw" for Traditional Chinese')
```
### process pdf: flag
```python=
# Convert a value in str to bool
def str2bool(s: str) -> bool:
if s and s.strip().lower() in ['true', 't', 'yes', 'y', '1']:
return True
return False
parser.add_argument(
'-p', '--pdf', action='store_true',
default=str2bool(os.getenv('PROCESS_PDF', 'False')),
help='Flag to process PDFs and convert to images')
```
### choices (限定選項)
```
parser.add_argument(
'-n', '--output_dir_name', type=str, required=True,
choices=['output-text', 'output-640x960', 'output-1920x1080'],
help='Specify the dir name for output (choose from "output-text", "output-640x960", "output-1920x1080")'
)
```
<br>
## 討論
### action='store_true' 是什麼意思?
- gpt-4o-2024-08-06
`action='store_true'` 是 `argparse` 模組中用來定義布林(Boolean)旗標參數的選項。當你在命令列中提供這個參數時,該參數的值會被設置為 `True`,如果沒有提供,則預設為 `False`。
這樣的參數通常用於開啟或關閉某個功能,類似於開關。
### 具體說明:
- **如果使用者在命令列輸入了 `--pdf`**,則 `args.pdf` 會被設為 `True`。
- **如果使用者沒有輸入 `--pdf`**,則 `args.pdf` 會被設為 `False`。
### 範例:
假設有以下程式碼:
```python=
parser = argparse.ArgumentParser(description="Process some PDFs.")
parser.add_argument('--pdf', action='store_true', help="Enable PDF processing")
args = parser.parse_args()
if args.pdf:
print("PDF processing enabled")
else:
print("PDF processing not enabled")
```
在命令列中:
1. 執行 `python script.py --pdf` 會印出:
```
PDF processing enabled
```
2. 執行 `python script.py`(不帶 `--pdf` 參數)會印出:
```
PDF processing not enabled
```
### 結論:
`action='store_true'` 的作用是當你在命令列中輸入該選項(如 `--pdf`),它的值會自動變成 `True`;若未輸入,則保持為 `False`。
<br>
### `parser.add_argument` 的 `action` 參數,要如何使用?
- gpt-4o-2024-08-06
在 Python 的 `argparse` 模組中,`action` 參數用來決定當命令行解析器遇到某個選項時應該執行什麼操作。`action` 有多種內建的選項,適用於不同情況,例如設定布林值、累積計數、儲存常量值等。
### `action` 參數的常見使用方法:
1. **`store`**(預設值):將選項後面的值存儲在對應的變數中。
- 預設情況下,`argparse` 的行為就是 `store`,即將參數的值儲存到對應變數中。
```python
parser.add_argument('--foo', action='store') # 默認行為
```
2. **`store_true` 和 `store_false`**:當選項被指定時,將布爾值 `True` 或 `False` 儲存到變數中。
- 常用於處理布爾開關參數,例如開啟或關閉某個功能。
```python
parser.add_argument('--verbose', action='store_true', help='enable verbose output')
parser.add_argument('--no-verbose', action='store_false', help='disable verbose output')
```
當使用 `--verbose` 時,`verbose` 變數會設為 `True`,不指定時為 `False`。同理,`--no-verbose` 會設為 `False`。
3. **`store_const`**:將指定的常量值存儲在變數中。
- 用於當你希望參數出現時,變數的值是一個特定的常量,而不是來自命令行的輸入。
```python
parser.add_argument('--foo', action='store_const', const=42, help='set foo to 42')
```
在這種情況下,如果指定了 `--foo`,則 `foo` 變數的值會被設置為 `42`。
4. **`append`**:將選項後面的值追加到一個列表中。
- 如果你希望同一個選項能夠多次出現並存儲多個值,可以使用 `append`。
```python
parser.add_argument('--foo', action='append', help='append items to the list')
```
使用方法如 `--foo 1 --foo 2 --foo 3`,最後 `foo` 變數會變成 `[1, 2, 3]`。
5. **`append_const`**:將常量追加到一個列表中。
- 和 `store_const` 類似,但會將常量值追加到一個列表中。
```python
parser.add_argument('--add-foo', action='append_const', const='foo', help='append "foo" to the list')
parser.add_argument('--add-bar', action='append_const', const='bar', help='append "bar" to the list')
```
使用 `--add-foo --add-bar` 時,列表會變成 `['foo', 'bar']`。
6. **`count`**:計數指定參數的出現次數,並將其存儲為整數。
- 常用於像 `-v` 這樣的選項,用來增加詳細程度,出現次數越多,詳情級別越高。
```python
parser.add_argument('-v', '--verbose', action='count', help='increase verbosity level')
```
使用 `-v -vv`,`verbose` 變數會根據 `-v` 的次數來增加數值。
7. **`help`** 和 **`version`**:內建的特殊 `action`,用於自動顯示幫助訊息或版本資訊。
```python
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
```
使用 `--version` 會輸出類似 `myprogram 1.0` 的訊息。
### 範例
假設你想實現一個腳本,它有以下功能:
1. 如果使用 `--verbose`,將顯示更多詳細信息。
2. 可以通過 `--output` 指定輸出文件。
3. 可以用 `--append` 追加多個項目到列表中。
以下是相應的代碼:
```python=
import argparse
parser = argparse.ArgumentParser(description='Sample parser with action parameter.')
# store_true 來處理布爾開關
parser.add_argument('--verbose', action='store_true', help='enable verbose output')
# 指定文件輸出,默認行為是 'store'
parser.add_argument('--output', action='store', help='specify output file')
# append 將多次出現的選項加入到列表中
parser.add_argument('--append', action='append', help='append items to the list')
args = parser.parse_args()
if args.verbose:
print("Verbose mode is on.")
if args.output:
print(f"Output will be written to {args.output}")
if args.append:
print(f"Items: {args.append}")
```
這段代碼展示了如何使用 `action` 參數來處理不同的選項行為。
- ### `version='%(prog)s 1.0'`,`%(prog)s` 的 `s` 是什麼意思?
在 `argparse` 中,`%(prog)s` 是一個佔位符,它表示當前程序的名稱。這個佔位符會自動替換為當前腳本的名字,即執行該腳本時的程式名。
具體來說,`%(prog)s` 中的 `s` 是 Python 標準格式化字符串語法的一部分,用於將變量格式化為字符串。在這裡,它表示將 `prog` 的值(即程序名稱)格式化為一個字符串。
這個佔位符來自 `argparse.ArgumentParser` 中的 `prog` 屬性,它默認是執行腳本時使用的名稱。如果你沒有指定 `prog`,`argparse` 會自動使用腳本的名稱。
例如,如果你的腳本名為 `my_script.py`,並且你使用了這個參數:
```
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
```
執行 `python my_script.py --version` 時,會顯示:
```
my_script.py 1.0
```
這裡的 `my_script.py` 是自動替換的程序名稱,`%(prog)s` 就是相當於 `my_script.py`。
<br>
### Python argparse 使用 sub-commands(子命令)範例
- ### Question
python 的 argparse.ArgumentParser
有 sub-program 的用法嗎?
就是 sub-program 有自己的參數 arg1, arg2, arg3
- ### gpt-4o-2024-08-06
是的,Python 的 `argparse` 模組支援 `sub-commands`(子命令),每個 `sub-command` 可以有自己的參數,像是 `arg1`、`arg2`、`arg3`。這是使用 `argparse.ArgumentParser` 和 `add_subparsers()` 方法來實現的。
以下是一個範例,展示如何定義主命令和子命令,每個子命令都有自己的參數:
```python=
import argparse
# 創建主解析器
parser = argparse.ArgumentParser(description="Main Program with Sub-commands")
subparsers = parser.add_subparsers(dest='subcommand', help='Sub-commands help')
# 定義第一個子命令 'sub1'
parser_sub1 = subparsers.add_parser('sub1', help='Sub-program 1')
parser_sub1.add_argument('--arg1', type=int, help='Argument 1 for sub1')
# 定義第二個子命令 'sub2'
parser_sub2 = subparsers.add_parser('sub2', help='Sub-program 2')
parser_sub2.add_argument('--arg2', type=str, help='Argument 2 for sub2')
# 定義第三個子命令 'sub3'
parser_sub3 = subparsers.add_parser('sub3', help='Sub-program 3')
parser_sub3.add_argument('--arg3', type=float, help='Argument 3 for sub3')
# 解析參數
args = parser.parse_args()
# 根據子命令執行不同的操作
if args.subcommand == 'sub1':
print(f"Sub-program 1, arg1: {args.arg1}")
elif args.subcommand == 'sub2':
print(f"Sub-program 2, arg2: {args.arg2}")
elif args.subcommand == 'sub3':
print(f"Sub-program 3, arg3: {args.arg3}")
else:
parser.print_help()
```
## 說明:
- `add_subparsers()` 用於創建一個子命令的容器,並將子命令附加到主解析器中。
- 每個子命令都可以透過 `add_parser()` 方法創建,並且可以為每個子命令定義自己的參數。
- `subcommand` 是透過 `dest='subcommand'` 設定的,這會根據用戶輸入的子命令名稱來識別。
## 執行範例:
```python=
# 執行子命令 'sub1' 並提供 --arg1 參數
python script.py sub1 --arg1 123
# 執行子命令 'sub2' 並提供 --arg2 參數
python script.py sub2 --arg2 hello
# 執行子命令 'sub3' 並提供 --arg3 參數
python script.py sub3 --arg3 3.14
```
這樣每個子命令都可以有自己的參數和邏輯,像是你所說的 `sub-program`。
<br>
### vars(...) 用途為何?
- ### gpt-4o-2024-08-06
`vars()` 是一個內建函式,它會返回某個對象的屬性字典,該字典將屬性名稱對應到它們的值。在 `argparse` 的上下文中,`vars()` 通常被用來將解析後的參數(以 `Namespace` 對象的形式)轉換成一個字典,方便進行檢查或處理。
在你的程式中,使用 `vars(args)` 的主要目的是將 `args`(解析命令列參數後的 `Namespace` 對象)轉換為一個字典。這樣,你可以檢查所有參數的值,看看是否有任何參數被提供。
範例如下:
```
args = parser.parse_args()
print(vars(args))
```
假設你執行的命令為:
```
python args_builder.py pdf2img -i ./input --pdf-zoom-factor 2.0
```
`vars(args)` 可能會返回這樣的字典:
```
{
'input_dir': './input',
'pdf_zoom_factor': 2.0,
'pdf_file': None,
'subcommand': 'pdf2img'
}
```
這樣,`vars(args)` 就可以讓你以字典形式來檢查所有參數是否有值,例如使用 `any(vars(args).values())` 來判斷是否有任何參數值被提供。
總結來說,`vars()` 用於將對象(這裡是 `Namespace`)轉換成字典,以便更靈活地檢查或操作它的屬性。