آموزش Argparse

نویسنده:

Tshepang Mbambo

این آموزش در نظر دارد مقدمه‌ای ملایم به argparse، ماژول توصیه‌شده برای تجزیه خط فرمان در کتابخانه استاندارد پایتون، باشد.

توجه

کتابخانه استاندارد شامل دو کتابخانه دیگر است که مستقیماً به پردازش پارامترهای خط فرمان مرتبط هستند: ماژول optparse در سطح پایین‌تر (که ممکن است برای پیکربندی یک برنامه مشخص به کد بیشتری نیاز داشته باشد، اما همچنین به برنامه اجازه می‌دهد رفتارهایی را درخواست کند که argparse از آن‌ها پشتیبانی نمی‌کند)، و getopt بسیار سطح پایین (که مشخصاً به‌عنوان معادلی برای خانواده توابع getopt() در دسترس برنامه‌نویسان C عمل می‌کند). اگرچه هیچ‌کدام از این ماژول‌ها مستقیماً در این راهنما پوشش داده نمی‌شوند، بسیاری از مفاهیم اصلی argparse ابتدا در optparse پدید آمدند، بنابراین برخی از جنبه‌های این آموزش برای کاربران optparse نیز مرتبط خواهند بود.

مفاهیم

بیایید با استفاده از دستور ls، نوع قابلیت‌هایی را که در این آموزش مقدماتی بررسی خواهیم کرد، نشان دهیم:

$ ls
cpython  devguide  prog.py  pypy  rm-unused-function.patch
$ ls pypy
ctypes_configure  demo  dotviewer  include  lib_pypy  lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x  4 wena wena 4096 Feb  8 12:04 devguide
-rwxr-xr-x  1 wena wena  535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb  7 00:59 pypy
-rw-r--r--  1 wena wena  741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...

چند مفهوم که می‌توانیم از ۴ دستور بیاموزیم:

  • فرمان ls زمانی که بدون هیچ گزینه‌ای اجرا شود، مفید است. این فرمان به‌طور پیش‌فرض محتویات پوشه جاری را نمایش می‌دهد.

  • اگر بخواهیم فراتر از آنچه به‌طور پیش‌فرض فراهم می‌کند، کمی بیشتر به آن می‌گوییم. در این حالت، می‌خواهیم پوشه‌ای متفاوت، یعنی pypy را نمایش دهد. کاری که انجام دادیم مشخص کردن چیزی است که به‌عنوان آرگومان جایگاهی شناخته می‌شود. این‌گونه نام‌گذاری شده است زیرا برنامه باید صرفاً بر اساس محل قرارگیری آن در خط فرمان بداند با مقدار چه کاری انجام دهد. این مفهوم بیشتر به دستوری مانند cp مربوط است، که ابتدایی‌ترین کاربرد آن cp SRC DEST است. جایگاه اول چیزی است که می‌خواهید کپی شود، و جایگاه دوم جایی است که می‌خواهید به آن کپی شود.

  • حال، فرض کنید می‌خواهیم رفتار برنامه را تغییر دهیم. در مثال خود، به‌جای نمایش فقط نام پرونده‌ها، اطلاعات بیشتری برای هر پرونده نمایش می‌دهیم. در این حالت، -l به‌عنوان یک آرگومان اختیاری شناخته می‌شود.

  • این قطعه‌ای از متن راهنما است. این بسیار مفید است، زیرا می‌توانید با برنامه‌ای مواجه شوید که هرگز پیش‌تر از آن استفاده نکرده‌اید و صرفاً با خواندن متن راهنمای آن بفهمید که چگونه کار می‌کند.

مبانی

بیایید با مثال بسیار ساده‌ای شروع کنیم که (تقریباً) هیچ کاری انجام نمی‌دهد:

import argparse
parser = argparse.ArgumentParser()
parser.parse_args()

نتیجه‌ی اجرای کد به شرح زیر است:

$ python prog.py
$ python prog.py --help
usage: prog.py [-h]

options:
  -h, --help  show this help message and exit
$ python prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo

آنچه اتفاق می‌افتد به این صورت است:

  • اجرای اسکریپت بدون هیچ گزینه‌ای باعث می‌شود چیزی در stdout نمایش داده نشود. چندان مفید نیست.

  • دومی شروع به نمایش سودمندی ماژول argparse می‌کند. ما تقریباً هیچ کاری انجام نداده‌ایم، اما از همین حالا یک پیام راهنمای مناسب دریافت می‌کنیم.

  • گزینه‌ی --help، که می‌توان آن را به -h نیز مخفف کرد، تنها گزینه‌ای است که به‌رایگان در اختیار داریم (یعنی نیازی به مشخص کردن آن نیست). مشخص کردن هر چیز دیگری منجر به خطا می‌شود. اما حتی در آن حالت نیز یک پیام کاربرد مفید، به‌رایگان دریافت می‌کنیم.

معرفی آرگومان‌های جایگاهی

یک مثال:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)

و اجرای کد:

$ python prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python prog.py --help
usage: prog.py [-h] echo

positional arguments:
  echo

options:
  -h, --help  show this help message and exit
$ python prog.py foo
foo

آنچه در حال رخ دادن است:

  • ما متد add_argument() را اضافه کرده‌ایم، که از آن برای مشخص کردن این‌که برنامه مایل به پذیرش کدام گزینه‌های خط فرمان است، استفاده می‌کنیم. در این مورد، من آن را echo نام‌گذاری کرده‌ام تا با تابع خود هماهنگ باشد.

  • اکنون برای فراخوانی برنامه‌ی خود باید یک گزینه را مشخص کنیم.

  • متد parse_args() در واقع مقداری داده از گزینه‌های مشخص‌شده برمی‌گرداند، در این حالت، echo.

  • این متغیر نوعی «جادو» است که argparse بدون نیاز به اقدام اضافه از سوی شما انجام می‌دهد (یعنی نیازی نیست مشخص کنید آن مقدار در کدام متغیر ذخیره می‌شود). همچنین متوجه خواهید شد که نام آن با آرگومان رشته‌ای داده‌شده به متد، echo، مطابقت دارد.

با این حال توجه داشته باشید که، اگرچه نمایش راهنما زیبا به نظر می‌رسد، اما در حال حاضر آن‌قدر که می‌تواند مفید نیست. برای مثال می‌بینیم که echo را به‌عنوان یک آرگومان جایگاهی داریم، اما نمی‌دانیم چه کاری انجام می‌دهد، مگر با حدس زدن یا خواندن کد منبع. بنابراین، بیایید آن را کمی مفیدتر کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)

و این نتیجه را می‌گیریم:

$ python prog.py -h
usage: prog.py [-h] echo

positional arguments:
  echo        echo the string you use here

options:
  -h, --help  show this help message and exit

حال، چطور است کاری حتی مفیدتر انجام دهیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)

نتیجه‌ی اجرای کد به شرح زیر است:

$ python prog.py 4
Traceback (most recent call last):
  File "prog.py", line 5, in <module>
    print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

این کار چندان خوب پیش نرفت. دلیلش این است که argparse گزینه‌هایی را که به آن می‌دهیم به‌صورت رشته در نظر می‌گیرد، مگر اینکه طور دیگری به آن بگوییم. پس، بیایید به argparse بگوییم آن ورودی را به‌صورت عدد صحیح در نظر بگیرد:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
                    type=int)
args = parser.parse_args()
print(args.square**2)

نتیجه‌ی اجرای کد به شرح زیر است:

$ python prog.py 4
16
$ python prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'

این به‌خوبی پیش رفت. برنامه اکنون حتی در صورت ورودی بد و غیرمجاز، پیش از ادامه به‌صورت مفیدی خارج می‌شود.

معرفی آرگومان‌های اختیاری

تاکنون با آرگومان‌های جایگاهی کار کرده‌ایم. بگذارید نگاهی بیندازیم به چگونگی افزودن آرگومان‌های اختیاری:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
    print("verbosity turned on")

و خروجی:

$ python prog.py --verbosity 1
verbosity turned on
$ python prog.py
$ python prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]

options:
  -h, --help            show this help message and exit
  --verbosity VERBOSITY
                        increase output verbosity
$ python prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument

آنچه اتفاق می‌افتد به این صورت است:

  • این برنامه به‌گونه‌ای نوشته شده است که هنگام مشخص شدن --verbosity چیزی نمایش دهد و در غیر این صورت هیچ چیز نمایش ندهد.

  • برای نشان دادن این که گزینه واقعاً اختیاری است، هنگام اجرای برنامه بدون آن هیچ خطایی رخ نمی‌دهد. توجه داشته باشید که به‌طور پیش‌فرض، اگر یک آرگومان اختیاری استفاده نشود، به متغیر مربوطه، در این مورد args.verbosity، مقدار None داده می‌شود، که به همین دلیل در آزمون درستی دستور if مردود می‌شود.

  • پیام راهنما کمی متفاوت است.

  • هنگام استفاده از گزینه --verbosity، باید مقداری را نیز مشخص کنید؛ هر مقداری.

مثال بالا هر مقدار عدد صحیحی را برای --verbosity می‌پذیرد، اما برای برنامه ساده ما، تنها دو مقدار واقعاً مفید هستند: True یا False. بیایید کد را بر همین اساس اصلاح کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
                    action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

و خروجی:

$ python prog.py --verbose
verbosity turned on
$ python prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python prog.py --help
usage: prog.py [-h] [--verbose]

options:
  -h, --help  show this help message and exit
  --verbose   increase output verbosity

آنچه اتفاق می‌افتد به این صورت است:

  • این گزینه اکنون بیشتر یک پرچم است تا چیزی که به یک مقدار نیاز داشته باشد. ما حتی نام گزینه را برای هماهنگی با همین ایده تغییر دادیم. توجه کنید که اکنون یک کلیدواژه جدید، action، مشخص می‌کنیم و مقدار "store_true" را به آن می‌دهیم. این بدان معناست که اگر گزینه مشخص شده باشد، مقدار True به args.verbose اختصاص داده می‌شود. مشخص نکردن آن به‌معنای False است.

  • هنگامی که مقداری را تعیین می‌کنید، اعتراض می‌کند؛ دقیقاً مطابق با ماهیت واقعی پرچم‌ها (flags).

  • به متن راهنمای متفاوت توجه کنید.

گزینه‌های کوتاه

اگر با استفاده از خط فرمان آشنا باشید، متوجه خواهید شد که هنوز به موضوع شکل‌های کوتاه گزینه‌ها نپرداخته‌ام. این موضوع کاملاً ساده است:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
                    action="store_true")
args = parser.parse_args()
if args.verbose:
    print("verbosity turned on")

و به این صورت است:

$ python prog.py -v
پرگویی روشن شد
$ python prog.py --help
طرز استفاده: prog.py [-h] [-v]

گزینه‌ها:
  -h, --help     نمایش این پیام راهنما و خروج
  -v, --verbose  افزایش پرگویی خروجی

توجه داشته باشید که قابلیت جدید نیز در متن راهنما منعکس شده است.

ترکیب آرگومان‌های جایگاهی و اختیاری

برنامه‌ی ما از نظر پیچیدگی همچنان در حال رشد است:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
    print(f"the square of {args.square} equals {answer}")
else:
    print(answer)

و اکنون خروجی:

$ python prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python prog.py 4
16
$ python prog.py 4 --verbose
the square of 4 equals 16
$ python prog.py --verbose 4
the square of 4 equals 16
  • ما یک آرگومان جایگاهی را بازگردانده‌ایم، به همین دلیل این شکایت وجود دارد.

  • توجه داشته باشید که ترتیب مهم نیست.

چطور است که توانایی داشتن چندین مقدار پرگویی (verbosity) را به این برنامه‌مان بازگردانیم و واقعاً از آن‌ها استفاده کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

و خروجی:

$ python prog.py 4
16
$ python prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python prog.py 4 -v 1
4^2 == 16
$ python prog.py 4 -v 2
the square of 4 equals 16
$ python prog.py 4 -v 3
16

همه‌ی این‌ها خوب به نظر می‌رسند، به‌جز آخرین مورد که یک خطا را در برنامه‌ی ما آشکار می‌کند. بیایید آن را با محدود کردن مقادیری که گزینه‌ی --verbosity می‌تواند بپذیرد، اصلاح کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

و خروجی:

$ python prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square

positional arguments:
  square                display a square of a given number

options:
  -h, --help            show this help message and exit
  -v, --verbosity {0,1,2}
                        increase output verbosity

توجه داشته باشید که این تغییر هم در پیام خطا و هم در رشته راهنما منعکس می‌شود.

اکنون، بیایید از رویکرد متفاوتی برای بازی با سطح جزئیات استفاده کنیم، که نسبتاً رایج است. این روش همچنین با شیوه‌ای که پرونده اجرایی CPython آرگومان سطح جزئیات خود را مدیریت می‌کند مطابقت دارد (خروجی python --help را بررسی کنید):

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

ما اکشن دیگری به نام "count" برای شمارش تعداد دفعات استفاده از گزینه‌های خاص معرفی کرده‌ایم.

$ python prog.py 4
16
$ python prog.py 4 -v
4^2 == 16
$ python prog.py 4 -vv
the square of 4 equals 16
$ python prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python prog.py 4 -h
usage: prog.py [-h] [-v] square

positional arguments:
  square           display a square of a given number

options:
  -h, --help       show this help message and exit
  -v, --verbosity  increase output verbosity
$ python prog.py 4 -vvv
16
  • بله، اکنون در نسخه‌ی پیشین اسکریپت ما، بیشتر حکم یک پرچم را دارد (مشابه action="store_true"). این باید دلیل آن شکایت را توضیح دهد.

  • همچنین رفتاری مشابه اکشن "store_true" دارد.

  • اکنون در اینجا نمونه‌ای از آنچه کنش «count» ارائه می‌دهد آمده است. شما احتمالاً پیش‌تر این نوع استفاده را دیده‌اید.

  • و اگر پرچم -v را مشخص نکنید، آن پرچم دارای مقدار None در نظر گرفته می‌شود.

  • همان‌طور که انتظار می‌رود، با مشخص کردن شکل بلند پرچم، باید همان خروجی را دریافت کنیم.

  • متأسفانه، خروجی راهنمای ما چندان درباره قابلیت جدیدی که اسکریپت ما کسب کرده است، گویا نیست، اما همیشه می‌توان این مشکل را با بهبود مستندات اسکریپت خود برطرف کرد (مثلاً از طریق آرگومان کلیدواژه‌ای help).

  • آن آخرین خروجی، اشکالی را در برنامه‌ی ما آشکار می‌کند.

بیایید آن را اصلاح کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2

# bugfix: replace == with >=
if args.verbosity >= 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

و این چیزی است که به دست می‌دهد:

$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python prog.py 4
Traceback (most recent call last):
  File "prog.py", line 11, in <module>
    if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
  • خروجی نخست به‌خوبی پیش رفت و اشکالی را که پیش‌تر داشتیم برطرف می‌کند. یعنی می‌خواهیم هر مقدار >= 2 تا حد ممکن پُرگویی داشته باشد.

  • خروجی سوم چندان خوب نیست.

بیایید آن اشکال را برطرف کنیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
    print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.square}^2 == {answer}")
else:
    print(answer)

ما همین حالا کلیدواژه‌ی دیگری به نام default را معرفی کردیم. ما آن را روی 0 تنظیم کرده‌ایم تا بتوان آن را با سایر مقادیر عدد صحیح مقایسه کرد. به خاطر داشته باشید که به‌طور پیش‌فرض، اگر آرگومان اختیاری مشخص نشده باشد، مقدار None را دریافت می‌کند و نمی‌توان آن را با یک مقدار عدد صحیح مقایسه کرد (از این رو استثنای TypeError رخ می‌دهد).

و:

$ python prog.py 4
16

شما می‌توانید فقط با آنچه تاکنون آموخته‌ایم بسیار پیش بروید، و ما تنها سطح آن را خراش داده‌ایم. ماژول argparse بسیار قدرتمند است و پیش از پایان این آموزش، کمی بیشتر آن را بررسی خواهیم کرد.

کمی پیشرفته‌تر

اگر بخواهیم برنامه‌ی کوچک خود را برای محاسبه‌ی توان‌های دیگر، نه فقط مربع‌ها، گسترش دهیم:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.x}^{args.y} == {answer}")
else:
    print(answer)

خروجی:

$ python prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python prog.py -h
usage: prog.py [-h] [-v] x y

positional arguments:
  x                the base
  y                the exponent

options:
  -h, --help       show this help message and exit
  -v, --verbosity
$ python prog.py 4 2 -v
4^2 == 16

توجه کنید که تاکنون از سطح پرگویی برای تغییر متنی که نمایش داده می‌شود استفاده کرده‌ایم. مثال زیر به‌جای آن از سطح پرگویی برای نمایش متن بیشتر استفاده می‌کند:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
    print(f"Running '{__file__}'")
if args.verbosity >= 1:
    print(f"{args.x}^{args.y} == ", end="")
print(answer)

خروجی:

$ python prog.py 4 2
16
$ python prog.py 4 2 -v
4^2 == 16
$ python prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16

مشخص کردن آرگومان‌های مبهم

هنگامی که در تشخیص اینکه یک آرگومان جایگاهی است یا برای یک آرگومان است، ابهام وجود داشته باشد، می‌توان از -- استفاده کرد تا به parse_args() گفته شود که همه‌چیز پس از آن یک آرگومان جایگاهی است:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')

>>> # ambiguous, so parse_args assumes it's an option
>>> parser.parse_args(['-f'])
usage: PROG [-h] [-n N [N ...]] [args ...]
PROG: error: unrecognized arguments: -f

>>> parser.parse_args(['--', '-f'])
Namespace(args=['-f'], n=None)

>>> # ambiguous, so the -n option greedily accepts arguments
>>> parser.parse_args(['-n', '1', '2', '3'])
Namespace(args=[], n=['1', '2', '3'])

>>> parser.parse_args(['-n', '1', '--', '2', '3'])
Namespace(args=['2', '3'], n=['1'])

گزینه‌های متعارض

تاکنون، با دو متد از یک نمونه‌ی argparse.ArgumentParser کار کرده‌ایم. بیایید متد سومی را معرفی کنیم، add_mutually_exclusive_group(). این متد به ما امکان می‌دهد گزینه‌هایی را مشخص کنیم که با یکدیگر ناسازگارند. بیایید همچنین سایر بخش‌های برنامه را تغییر دهیم تا قابلیت جدید منطقی‌تر باشد: گزینه‌ی --quiet را معرفی خواهیم کرد، که متضاد گزینه‌ی --verbose خواهد بود:

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print(answer)
elif args.verbose:
    print(f"{args.x} to the power {args.y} equals {answer}")
else:
    print(f"{args.x}^{args.y} == {answer}")

برنامه‌ی ما اکنون ساده‌تر است و برخی قابلیت‌ها را به‌منظور نمایش از دست داده‌ایم. به هر حال، خروجی به این صورت است:

$ python prog.py 4 2
4^2 == 16
$ python prog.py 4 2 -q
16
$ python prog.py 4 2 -v
4 to the power 2 equals 16
$ python prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose

این باید به‌راحتی قابل‌فهم باشد. آن خروجی پایانی را افزوده‌ام تا بتوانید نوع انعطاف‌پذیری‌ای را که در اختیار دارید ببینید، یعنی ترکیب گزینه‌های بلندبا با گزینه‌های کوتاه‌با.

پیش از آنکه به پایان برسیم، احتمالاً می‌خواهید هدف اصلی برنامه‌ی خود را به کاربران خود بگویید، فقط برای حالتی که نمی‌دانند:

import argparse

parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y

if args.quiet:
    print(answer)
elif args.verbose:
    print(f"{args.x} to the power {args.y} equals {answer}")
else:
    print(f"{args.x}^{args.y} == {answer}")

توجه کنید که تفاوت اندکی در متن کاربرد وجود دارد. به [-v | -q] توجه کنید، که به ما می‌گوید می‌توانیم از -v یا -q استفاده کنیم، اما نه هر دو به‌طور همزمان:

$ python prog.py --help
نحوه استفاده: prog.py [-h] [-v | -q] x y

X را به توان Y محاسبه می‌کند

آرگومان‌های جایگاهی:
  x              پایه
  y              توان

گزینه‌ها:
  -h, --help     نمایش این پیام راهنما و خروج
  -v, --verbose
  -q, --quiet

چگونه خروجی argparse را ترجمه کنیم

خروجی‌های ماژول argparse مانند متن راهنما و پیام‌های خطای آن، همگی با استفاده از ماژول gettext قابل ترجمه می‌شوند. این امکان را به برنامه‌ها می‌دهد تا پیام‌های تولیدشده توسط argparse را به‌سادگی بومی‌سازی کنند. همچنین بین‌المللی‌سازی برنامه‌ها و ماژول‌های شما را ببینید.

برای مثال، در این خروجی argparse:

$ python prog.py --help
نحوه استفاده: prog.py [-h] [-v | -q] x y

X را به توان Y محاسبه می‌کند

آرگومان‌های جایگاهی:
  x              پایه
  y              توان

گزینه‌ها:
  -h, --help     نمایش این پیام راهنما و خروج
  -v, --verbose
  -q, --quiet

رشته‌های usage:، positional arguments:، options: و show this help message and exit همگی قابل ترجمه هستند.

برای ترجمه‌ی این رشته‌ها، ابتدا باید آن‌ها را به یک پرونده .po استخراج کنید. برای مثال، با استفاده از Babel، این فرمان را اجرا کنید:

$ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py

این دستور تمام رشته‌های ترجمه‌پذیر را از ماژول argparse استخراج می‌کند و آن‌ها را در پرونده‌ای به نام messages.po خروجی می‌دهد. این دستور فرض می‌کند که نصب پایتون شما در /usr/lib قرار دارد.

شما می‌توانید با استفاده از این اسکریپت، محل ماژول argparse را در سیستم خود بیابید:

import argparse
print(argparse.__file__)

پس از اینکه پیام‌های موجود در پرونده .po ترجمه شدند و ترجمه‌ها با استفاده از gettext نصب شدند، argparse می‌تواند پیام‌های ترجمه‌شده را نمایش دهد.

برای ترجمه‌ی رشته‌های خودتان در خروجی argparse، از gettext استفاده کنید.

مبدل‌های نوع سفارشی

ماژول argparse به شما امکان می‌دهد مبدل‌های نوع سفارشی برای آرگومان‌های خط فرمان خود تعیین کنید. این امکان به شما اجازه می‌دهد ورودی کاربر را پیش از ذخیره شدن در argparse.Namespace تغییر دهید. این می‌تواند زمانی مفید باشد که نیاز دارید ورودی را پیش از استفاده در برنامه‌تان پیش‌پردازش کنید.

هنگام استفاده از یک مبدل نوع سفارشی، می‌توانید از هر فراخوانی‌پذیری که یک آرگومان رشته‌ای دریافت می‌کند (مقدار آرگومان) و مقدار تبدیل‌شده را برمی‌گرداند، استفاده کنید. با این حال، اگر نیاز به مدیریت حالت‌های پیچیده‌تر دارید، می‌توانید به‌جای آن از یک کلاس اکشنسفارشی با پارامتر action استفاده کنید.

برای مثال، فرض کنید می‌خواهید آرگومان‌هایی با پیشوندهای مختلف را مدیریت کنید و آن‌ها را بر همان اساس پردازش کنید:

import argparse

parser = argparse.ArgumentParser(prefix_chars='-+')

parser.add_argument('-a', metavar='<value>', action='append',
                    type=lambda x: ('-', x))
parser.add_argument('+a', metavar='<value>', action='append',
                    type=lambda x: ('+', x))

args = parser.parse_args()
print(args)

خروجی:

$ python prog.py -a value1 +a value2
Namespace(a=[('-', 'value1'), ('+', 'value2')])

در این مثال، ما:

  • یک پارسر با نویسه‌های پیشوند سفارشی با استفاده از پارامتر prefix_chars ایجاد شد.

  • دو آرگومان -a و +a تعریف شدند که از پارامتر type برای ایجاد مبدل‌های نوع سفارشی استفاده می‌کردند تا مقدار را در یک تاپلهمراه با پیشوند ذخیره کنند.

بدون مبدل‌های نوع سفارشی، -a و +a به‌عنوان یک آرگومان یکسان تلقی می‌شدند، که این موضوع نامطلوب بود. با استفاده از مبدل‌های نوع سفارشی، توانستیم بین این دو آرگومان تمایز قائل شویم.

نتیجه‌گیری

ماژول argparse امکانات بسیار بیشتری نسبت به آنچه در اینجا نشان داده شد ارائه می‌دهد. مستندات آن کاملاً دقیق و جامع و سرشار از مثال است. پس از گذراندن این آموزش، باید بتوانید به‌راحتی آن‌ها را درک کنید، بدون آن‌که احساس کنید مطالب برایتان سنگین است.