טיפול ב-command line arguments בעזרת argparse
המודול argparse
מאפשר לנו להוסיף בקלות ובמהירות טיפול ב-command line arguments לקוד Python אותו כתבנו. זוהו מודול נפוץ מאוד עם המון תיעוד באינטרנט, אבל חשבתי שיהיה נחמד לכתוב עליו הסבר יחסית מפורט לגבי השימוש הבסיסי.
הקדמה קצרה על מה זה Command line arguments
באופן כללי, command line arguments הם פרמטרים שנותנים לתוכנית כדי להשפיע על האופן שבו היא תרוץ. למשל, עבור הפקודה הלינוקסית ls
להצגת שמות הקבצים בתקייה הנוכחית, ניתן להוסיף את הדגל a-
כדי להציג קבצים נסתרים. בדוגמה הבאה ניתן לראות שכאשר נוסף הדגל a-
ישנם גם קבצים מוסתרים (במקרה זה .
ו-..
)
$ ls
file1 file2 file3
$ ls -a
. .. file1 file2 file3
פעמים רבות כאשר כותבים קוד python שמישהו באמת מריץ מה-command line זה עוזר להוסיף ארגומנטים שישפיעו על הצורה שבה התוכנית תרוץ. נשים לב כי יכולים להיות ארגומנטים שהם חובה, ויכולים להיות ארגומנטים אופציונליים (שלרוב נקראים דגלים או flag-ים).
שימוש בסיסי ב-argparse
השלבים המרכזיים בעת שימוש ב-argparse:
- ביצוע
import argparse
- יצירת parser
- הוספת ארגומנטים ל-parser שיצרנו
- פרסור הארגומנטים
- קריאה לפונקציית main עם הארגומנטים לאחר הפרסור
בדוגמה הבאה אנחנו יוצרים parser עם שני ארגומנטים אופציונאלים, a
,b
ואנחנו פשוט מדפיסים אותם ב-main.
# 1 - Importing argparse
import argparse
def main(args):
print("Flag a: {}".format(args.a))
print("Flag b: {}".format(args.b))
if __name__ == "__main__":
# 2 - Creating parser
parser = argparse.ArgumentParser()
# 3 - Adding arguments
parser.add_argument('-a', help="This is the flag a")
parser.add_argument('-b', help="This is the flag b")
# 4 - Parse arguments
args = parser.parse_args()
# 5 - Call main function
main(args)
דוגמאות הרצה:
$ python ./example.py -a 5 -b 6
Flag a: 5
Flag b: 6
python ./example.py -a bla -b 6
Flag a: bla
Flag b: 6
בנוסף, באופן אוטומטי התווסף לנו הדגל f-
שמוסיף הסבר על התוכנית והארגומנטים שלה.
$ python example.py -h
usage: example.py [-h] [-a A] [-b B]
optional arguments:
-h, --help show this help message and exit
-a A This is the flag a
-b B This is the flag b
ארגומנטים חובה (positional) ורשות (optional)
לפעמים ישנם ארגומנטים שאנחנו נחייב את המשתמש להעביר. למשל, עבור תוכנית שמעתיקה קבצים ממקום למקום אנחנו נדרוש מהמשתמש לתת נתיב של הקובץ שאותו נעתיק ונתיב של התיקייה אליה נעתיק. למרות זאת, ישנם ארגומנטים שהם אופציונאלים (דגלים) שהמשתמש יכול לבחור אם להעביר או לא, למשל האם להוסיף progress bar לתהליך של ההעתקה או ליצור גיבוי לקובץ לפני ההעתקה.
עבור פרמטר חובה, פשוט רושמים את השם שלו בתור הפרמטר הראשון ב-add_argument
.
עבור פרמטר אופציונאלי, נהוג להוסיף “גישה מקוצרת” בעזרת אות אחת וגם “גישה מלאה” בעזרת השם המלא. ב"גישה המקוצרת" מוסיפים מקף (-
) לפני השם המקוצר, וב"גישה המלאה" מוסיפים פעמיים מקף (--
) לפני השם המלא. אפשר שיהיה רק ה"גישה המקוצרת", רק את ה"גישה המלאה" או את שניהם. אני לרוב מוסיף את שניהם, אלא אם מדובר בתוכנית ענקית אם המון דגלים אופציונאלים.
בדוגמה הבאה אנחנו רק מטפלים בארגומנטים, ומעבירים אותם ל-main, אבל לא עושים איתם שום דבר.
שימו לב ל-args.progress_bar
. זהוי לא טעות, argparse
בעצמו ממיר את progress-bar
ל-progress_bar
(ההבדל הוא בין המקף לקו תחתון, שכן משתנה לא יכול להכיל מקף)
import argparse
def main(args):
# Arguments can be accessed by:
# args.source
# args.destination
# args.progress_bar
# args.backup
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('source', help='Source path')
parser.add_argument('destination', help='Destination path')
parser.add_argument('-p', '--progress-bar', help='Should show progress bar')
parser.add_argument('-b', '--backup', help='Should create file backup')
args = parser.parse_args()
main(args)
תכונות של הארגומנטים שאנחנו מוסיפים
argparse
מאפשר לנו לקבוע תכונות של הארגומנט אותו אנחנו מוסיפים. מבצעים זאת על ידי הוספת פרמטרים לפונקצייה add_argument
.
- help - הסבר על הפרמטר. השתמשנו בזה בדוגמה הבסיסת ללא הסבר, אך אני מניח שזה היה מספיק ברור.
- type - מאפשר לקבוע את הסוג של המשתנה שאנחנו מצפים לשמור, למשל,
int
אוstr
. אם המשתמש יריץ את התוכנית עם פרמטר לא מתאים, תיזרק שגיאה. - default - מאפשר לקבוע ערך ברירת מחדל למקרה שהמשתמש לא מעביר שום ערך.
- choices - מאפשר לקבוע את הערכים החוקיים שהמשתמש יכול להעביר (
list
אוtupple
של ערכים חוקיים) - action - מאפשר לקבוע פעולה שתתבצע על הארגומנט. ניתן ליצור פונקציה בעצמנו, אבל לדעתי השימוש הנפוץ ביותר הוא בעזרת
'store_true'
עם ערך ברירת מחדל שלFalse
. בצורה זו ניתן לבחור פעולה כלשהי שתתבצע רק אם המשתמש באמת ביקש אותה. נשים לב שדגל זה לא דורש פרמטר נוסף, רק צריך להעביר אותו או לא להעביר אותו.
דוגמה:
import argparse
def main(args):
print("number of runs: {}".format(args.number_of_runs))
print("name: {}".format(args.name))
print("restricted name: {}".format(args.restricted_name))
# Print argument1 only if -p/--print-info was passed
if args.print_info:
print("argument1: {}".format(args.argument1))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('argument1', type=int, help="Argument1 which must be passed")
parser.add_argument('-c', '--number-of-runs', type=int, default=5, help='Number of times to run script')
parser.add_argument('-n', '--name', type=str, default="Avi", help='Choose name to print')
parser.add_argument('--restricted-name', type=str, default="Avi", choices=['Avi', 'Benny'], help='Choose restricted name to print')
parser.add_argument('-p', '--print-info', action="store_true", default=False, help='Print information')
args = parser.parse_args()
main(args)
דוגמאות הרצה עם פרמטרים תקינים:
$ python example.py 123 --restricted-name "Benny"
number of runs: 5
name: Avi
restricted name: Benny
$ python example.py 123 --restricted-name "Benny" -p
number of runs: 5
name: Avi
restricted name: Benny
argument1: 123
קצת דוגמאות הרצה עם פרמטרים לא תקינים:
$ python example.py
usage: example.py [-h] [-c NUMBER_OF_RUNS] [-n NAME]
[--restricted-name {Avi,Benny}]
argumnt1
example.py: error: too few arguments
$ python example.py some_string
usage: example.py [-h] [-c NUMBER_OF_RUNS] [-n NAME]
[--restricted-name {Avi,Benny}]
argumnt1
example.py: error: argument argumnt1: invalid int value: 'some_string'
$ python example.py 123 --restricted-name "Amit"
usage: example.py [-h] [-c NUMBER_OF_RUNS] [-n NAME]
[--restricted-name {Avi,Benny}]
argumnt1
example.py: error: argument --restricted-name: invalid choice: 'Amit' (choose from 'Avi', 'Benny')
סיכום
המודול argparse מאפשר לנו להוסיף בקלות טיפול לארגומנטים בקוד python שלנו ולאפשר הרצה נוחה יותר עבור המשתמש. דיברתי כאן רק על שימוש בסיסי מאוד ב-argparse, לקריאה נוספת אני ממליץ לקרוא את הדוקומנטציה הרשמית.