آرگومان های خط فرمان پایتون


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

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

در پایان این آموزش، خواهید دانست:

  • ریشه آرگومان های خط فرمان پایتون
  • پشتیبانی زیربنایی از آرگومان های خط فرمان پایتون
  • استانداردهایی که طراحی رابط خط فرمان را هدایت می کنند
  • مبانی سفارشی سازی و مدیریت دستی آرگومان های خط فرمان پایتون
  • کتابخانه های موجود در پایتون برای سهولت در توسعه یک رابط پیچیده خط فرمان

اگر می خواهید روشی کاربرپسند برای ارائه آرگومان های خط فرمان پایتون به برنامه خود بدون وارد کردن یک کتابخانه اختصاصی داشته باشید، یا اگر می خواهید مبنای مشترک کتابخانه های موجود را که به ساخت رابط خط فرمان پایتون اختصاص داده شده اند، بهتر درک کنید، به خواندن ادامه دهید!

رابط خط فرمان

یک رابط خط فرمان (CLI) راهی را برای کاربر فراهم می کند تا با برنامه ای که در یک مفسر پوسته مبتنی بر متن اجرا می شود، تعامل داشته باشد. برخی از نمونه های مفسران پوسته Bash در لینوکس یا Command Prompt در ویندوز هستند. یک رابط خط فرمان توسط مفسر پوسته فعال می شود که یک خط فرمان را در معرض دید قرار می دهد. می توان آن را با عناصر زیر مشخص کرد:

  • یک دستور یا برنامه
  • آرگومان های خط فرمان صفر یا بیشتر
  • خروجی که نتیجه دستور را نشان می دهد
  • مستندات متنی که به آن استفاده یا کمک گفته می شود

هر رابط خط فرمان ممکن است همه این عناصر را ارائه دهد، اما این لیست نیز جامع نیست. پیچیدگی خط فرمان از توانایی ارسال یک آرگومان واحد گرفته تا آرگومان ها و گزینه های متعدد، بسیار شبیه به یک زبان خاص دامنه را شامل می شود. به عنوان مثال، برخی از برنامه ها ممکن است مستندات وب را از خط فرمان راه اندازی کنند یا یک مفسر پوسته تعاملی مانند پایتون را راه اندازی کنند.

دو مثال زیر با دستور پایتون توصیف یک رابط خط فرمان را نشان می دهد:

python -c "print('Real Python')"
Real Python

در این مثال اول، مفسر پایتون گزینه -c را برای دستور می گیرد، که می گوید آرگومان های خط فرمان پایتون را به دنبال گزینه -c به عنوان یک برنامه پایتون اجرا کنید.

مثال دیگر نحوه فراخوانی پایتون با -h را برای نمایش راهنما نشان می دهد:

python -h
usage: python3 [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-b     : issue warnings about str(bytes_instance), str(bytearray_instance)
         and comparing bytes/bytearray with str. (-bb: issue errors)
[ ... complete help text not shown ... ]

این را در ترمینال خود امتحان کنید تا مستندات کامل راهنما را ببینید.

میراث C

آرگومان های خط فرمان پایتون مستقیما از زبان برنامه نویسی C به ارث می برند. همانطور که Guido Van Rossum در مقدمه ای بر پایتون برای برنامه نویسان یونیکس/سی در سال 1993 نوشت، C تأثیر زیادی بر پایتون داشت. Guido تعاریف تحت اللفظی، شناسه ها، عملگرها و عباراتی مانند شکستن، ادامه یا بازگشت را ذکر می کند. استفاده از آرگومان های خط فرمان پایتون نیز به شدت تحت تأثیر زبان C است.

برای نشان دادن شباهت ها، برنامه C زیر را در نظر بگیرید:

// main.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Arguments count: %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("Argument %6d: %s\n", i, argv[i]);
    }
    return 0;
}

خط 4 main() را تعریف می کند که نقطه ورود یک برنامه C است. پارامترها را به خوبی یادداشت کنید:

  1. ARGC یک عدد صحیح است که تعداد آرگومان های برنامه را نشان می دهد.
  2. argv آرایه ای از اشاره گرها به کاراکترهای حاوی نام برنامه در اولین عنصر آرایه است و به دنبال آن آرگومان های برنامه، در صورت وجود، در عناصر باقی مانده آرایه قرار می گیرد.

می توانید کد بالا را در لینوکس با gcc -o main main.c کامپایل کنید، سپس با ./main اجرا کنید تا موارد زیر را بدست آورید:

gcc -o main main.c
./main
Arguments count: 1
Argument      0: ./main

مگر اینکه به صراحت در خط فرمان با گزینه -o بیان شده باشد، a.out نام پیش فرض فایل اجرایی تولید شده توسط کامپایلر gcc است. مخفف خروجی اسمبلر است و یادآور فایل های اجرایی است که در سیستم های قدیمی یونیکس تولید شده اند. توجه داشته باشید که نام فایل اجرایی ./main تنها استدلال است.

بیایید این مثال را با ارسال چند آرگومان خط فرمان پایتون به همان برنامه ادویه کنیم:

./main Python Command Line Arguments
Arguments count: 5
Argument      0: ./main
Argument      1: Python
Argument      2: Command
Argument      3: Line
Argument      4: Arguments

خروجی نشان می دهد که تعداد آرگومان ها 5 است و لیست آرگومان ها شامل نام برنامه، اصلی و به دنبال آن هر کلمه از عبارت "Python Command Line Arguments" است که در خط فرمان ارسال کرده اید.

کامپایل main.c فرض می کند که شما از سیستم لینوکس یا سیستم عامل مک استفاده کرده اید. در ویندوز، می توانید این برنامه C را با یکی از گزینه های زیر کامپایل کنید:

  • زیرسیستم ویندوز برای لینوکس (WSL): در چند توزیع لینوکس مانند اوبونتو، OpenSUSE و Debian و غیره در دسترس است. می توانید آن را از فروشگاه مایکروسافت نصب کنید.
  • ابزارهای ساخت ویندوز: این شامل ابزارهای ساخت خط فرمان ویندوز، cl.exe کامپایلر مایکروسافت C/C++ و یک فرانت اند کامپایلر به نام clang.exe برای C/C++ است.
  • مایکروسافت ویژوال استودیو: این اصلی ترین محیط توسعه یکپارچه مایکروسافت (IDE) است. برای کسب اطلاعات بیشتر در مورد IDE هایی که می توانند هم برای پایتون و هم برای C در سیستم عامل های مختلف از جمله ویندوز استفاده شوند، IDE های پایتون و ویرایشگرهای کد (راهنما) را بررسی کنید.
  • پروژه mingw-64: از کامپایلر GCC در ویندوز پشتیبانی می کند.

اگر Microsoft Visual Studio یا Windows Build Tools را نصب کرده اید، می توانید main.c را به صورت زیر کامپایل کنید:

C:/>cl main.c

شما یک فایل اجرایی به نام main.exe به دست خواهید آورد که می توانید با آن شروع کنید:

C:/>main
Arguments count: 1
Argument      0: main

شما می توانید یک برنامه پایتون را پیاده سازی کنید، main.py، که معادل برنامه C، main.c است، که در بالا دیدید:

main.py
import sys

if __name__ == "__main__":
    print(f"Arguments count: {len(sys.argv)}")
    for i, arg in enumerate(sys.argv):
        print(f"Argument {i:>6}: {arg}")

شما یک متغیر argc مانند مثال کد C نمی بینید. در پایتون وجود ندارد زیرا sys.argv کافی است. شما می توانید آرگومان های خط فرمان پایتون را در sys.argv بدون نیاز به دانستن طول لیست تجزیه کنید و در صورت نیاز به تعداد آرگومان ها توسط برنامه می توانید len() داخلی را فراخوانی کنید.

همچنین، توجه داشته باشید که enumerate()، هنگامی که روی یک تکرارپذیر اعمال می شود، یک شی enumerate را برمی گرداند که می تواند جفت هایی را منتشر کند که شاخص یک عنصر در sys.arg را به مقدار مربوطه آن مرتبط می کند. این اجازه می دهد تا محتوای sys.argv را بدون نیاز به حفظ شمارنده برای فهرست در لیست حلقه بزنید.

main.py را به شرح زیر اجرا کنید:

python main.py Python Command Line Arguments
Arguments count: 5
Argument      0: main.py
Argument      1: Python
Argument      2: Command
Argument      3: Line
Argument      4: Arguments

sys.argv حاوی همان اطلاعاتی است که در برنامه C وجود دارد:

  • نام برنامه main.py اولین مورد لیست است.
  • آرگومان های پایتون، فرمان، خط و آرگومان ها عناصر باقی مانده در لیست هستند.

با این مقدمه کوتاه در مورد چند جنبه مرموز زبان C، اکنون به دانش ارزشمندی برای درک بیشتر آرگومان های خط فرمان پایتون مجهز شده اید.

دو ابزار از دنیای یونیکس

برای استفاده از آرگومان های خط فرمان پایتون در این آموزش، برخی از ویژگی های جزئی دو ابزار را از اکوسیستم یونیکس پیاده سازی خواهید کرد:

  1. شای1سم
  2. دنباله

در بخش های بعدی با این ابزارهای یونیکس آشنا خواهید شد.

sha1sum

sha1sum هش های SHA-1 را محاسبه می کند و اغلب برای تأیید یکپارچگی فایل ها استفاده می شود. برای یک ورودی معین، یک تابع هش همیشه همان مقدار را برمی گرداند. هر گونه تغییر جزئی در ورودی منجر به مقدار هش متفاوتی می شود. قبل از استفاده از ابزار با پارامترهای مشخص، می توانید سعی کنید راهنما را نمایش دهید:

sha1sum --help
Usage: sha1sum [OPTION]... [FILE]...
Print or check SHA1 (160-bit) checksums.

With no FILE, or when FILE is -, read standard input.

  -b, --binary         read in binary mode
  -c, --check          read SHA1 sums from the FILEs and check them
      --tag            create a BSD-style checksum
  -t, --text           read in text mode (default)
  -z, --zero           end each output line with NUL, not newline,
                       and disable file name escaping
[ ... complete help text not shown ... ]

نمایش کمک یک برنامه خط فرمان یکی از ویژگی های رایج است که در رابط خط فرمان آشکار می شود.

برای محاسبه مقدار هش SHA-1 محتوای یک فایل، به شرح زیر عمل می کنید:

sha1sum main.c
125a0f900ff6f164752600550879cbfabb098bc3  main.c

نتیجه مقدار هش SHA-1 را به عنوان فیلد اول و نام فایل را به عنوان فیلد دوم نشان می دهد. این دستور می تواند بیش از یک فایل را به عنوان آرگومان بگیرد:

sha1sum main.c main.py
125a0f900ff6f164752600550879cbfabb098bc3  main.c
d84372fc77a90336b6bb7c5e959bcb1b24c608b4  main.py

به لطف ویژگی گسترش حروف عام ترمینال یونیکس، امکان ارائه آرگومان های خط فرمان پایتون با کاراکترهای عام نیز وجود دارد. یکی از این شخصیت ها ستاره یا ستاره (*) است:

sha1sum main.*
3f6d5274d6317d580e2ffc1bf52beee0d94bf078  main.c
f41259ea5835446536d2e71e566075c1c1bfc111  main.py

پوسته main.* را به main.c و main.py تبدیل می کند، که دو فایل مطابق با الگوی main.* در دایرکتوری فعلی هستند و آنها را به sha1sum ارسال می کند. این برنامه هش SHA1 هر یک از فایل های موجود در لیست آرگومان را محاسبه می کند. خواهید دید که در ویندوز، رفتار متفاوت است. ویندوز هیچ گسترش عام ندارد، بنابراین ممکن است برنامه مجبور شود آن را در خود جای دهد. پیاده سازی شما ممکن است نیاز به گسترش حروف عام در داخل داشته باشد.

بدون هیچ استدلالی، sha1sum از ورودی استاندارد خوانده می شود. با تایپ کاراکترها روی صفحه کلید می توانید داده ها را به برنامه تغذیه کنید. ورودی ممکن است هر کاراکتری را در خود جای دهد، از جمله بازگشت کالسکه Enter. برای خاتمه دادن به ورودی، باید انتهای فایل را با Enter و به دنبال آن دنباله Ctrl+D:

sha1sum
Real
Python
87263a73c98af453d68ee4aab61576b331f8d9d6  -

ابتدا نام برنامه sha1sum را وارد می کنید و سپس Enter و سپس Real و Python را وارد می کنید که هر کدام نیز Enter را وارد می کنند. برای بستن جریان ورودی، Ctrl+D را تایپ کنید. نتیجه مقدار هش SHA1 تولید شده برای متن Real\nPython\n است. نام فایل -. این یک قرارداد برای نشان دادن ورودی استاندارد است. مقدار هش هنگام اجرای دستورات زیر یکسان است:

python -c "print('Real\nPython\n', end='')" | sha1sum
87263a73c98af453d68ee4aab61576b331f8d9d6  -
python -c "print('Real\nPython')" | sha1sum
87263a73c98af453d68ee4aab61576b331f8d9d6  -
printf "Real\nPython\n" | sha1sum
87263a73c98af453d68ee4aab61576b331f8d9d6  -

در مرحله بعد، شرح کوتاهی از seq را خواهید خواند.

دنباله

Seq دنباله ای از اعداد را تولید می کند. در ابتدایی ترین شکل آن، مانند تولید دنباله از 1 تا 5، می توانید موارد زیر را اجرا کنید:

seq 5
1
2
3
4
5

برای دریافت یک نمای کلی از احتمالات در معرض seq، می توانید راهنما را در خط فرمان نمایش دهید:

seq --help
Usage: seq [OPTION]... LAST
  or:  seq [OPTION]... FIRST LAST
  or:  seq [OPTION]... FIRST INCREMENT LAST
Print numbers from FIRST to LAST, in steps of INCREMENT.

Mandatory arguments to long options are mandatory for short options too.
  -f, --format=FORMAT      use printf style floating-point FORMAT
  -s, --separator=STRING   use STRING to separate numbers (default: \n)
  -w, --equal-width        equalize width by padding with leading zeroes
      --help     display this help and exit
      --version  output version information and exit
[ ... complete help text not shown ... ]

برای این آموزش، چند نوع ساده شده از sha1sum و seq را خواهید نوشت. در هر مثال، جنبه یا ترکیبی متفاوت از ویژگی ها را در مورد آرگومان های خط فرمان پایتون یاد خواهید گرفت.

در سیستم عامل مک و لینوکس، sha1sum و seq باید از قبل نصب شده باشند، اگرچه ویژگی ها و اطلاعات راهنما ممکن است گاهی اوقات بین سیستم ها یا توزیع ها کمی متفاوت باشد. اگر از ویندوز 10 استفاده می کنید، راحت ترین روش اجرای sha1sum و seq در یک محیط لینوکس نصب شده در WSL است. اگر به ترمینالی که ابزارهای استاندارد یونیکس را در معرض دید قرار می دهد دسترسی ندارید، ممکن است به پایانه های آنلاین دسترسی داشته باشید:

  • یک حساب کاربری رایگان در PythonAnywhere ایجاد کنید و یک کنسول Bash راه اندازی کنید.
  • یک ترمینال موقت Bash در repl.it ایجاد کنید.

این دو نمونه است و ممکن است موارد دیگری را پیدا کنید.

آرایه sys.argv

قبل از بررسی برخی از قراردادهای پذیرفته شده و کشف نحوه مدیریت آرگومان های خط فرمان پایتون، باید بدانید که پشتیبانی اساسی برای تمام آرگومان های خط فرمان پایتون توسط sys.argv ارائه شده است. مثال های بخش های زیر به شما نشان می دهد که چگونه آرگومان های خط فرمان پایتون ذخیره شده در sys.argv را مدیریت کنید و بر مشکلات معمولی که هنگام تلاش برای دسترسی به آنها رخ می دهد غلبه کنید. یاد خواهید گرفت:

  • نحوه دسترسی به محتوای sys.argv
  • چگونه عوارض جانبی ماهیت جهانی sys.argv را کاهش دهیم؟
  • نحوه پردازش فضاهای خالی در آرگومان های خط فرمان پایتون
  • نحوه مدیریت خطاها هنگام دسترسی به آرگومان های خط فرمان پایتون
  • نحوه دریافت فرمت اصلی آرگومان های خط فرمان پایتون که توسط بایت ها ارسال شده است؟

بیا شروع کنیم!

نمایش آرگومان ها

ماژول sys آرایه ای به نام argv را در معرض دید قرار می دهد که شامل موارد زیر است:

  1. argv[0] شامل نام برنامه فعلی پایتون است.
  2. argv[1:]، بقیه لیست، شامل همه آرگومان های خط فرمان پایتون است که به برنامه ارسال شده است.

مثال زیر محتوای sys.argv را نشان می دهد:

argv.py
import sys

print(f"Name of the script      : {sys.argv[0]=}")
print(f"Arguments of the script : {sys.argv[1:]=}")

در اینجا نحوه عملکرد این کد آمده است:

  • خط 2 سیستم ماژول داخلی پایتون را وارد می کند.
  • خط 4 نام برنامه را با دسترسی به اولین عنصر لیست sys.argv استخراج می کند.
  • خط 5 آرگومان های خط فرمان پایتون را با واکشی تمام عناصر باقیمانده لیست sys.argv نمایش می دهد.

اسکریپت argv.py بالا را با لیستی از آرگومان های دلخواه به شرح زیر اجرا کنید:

python argv.py un deux trois quatre
Name of the script      : sys.argv[0]='argv.py'
Arguments of the script : sys.argv[1:]=['un', 'deux', 'trois', 'quatre']

خروجی تأیید می کند که محتوای sys.argv[0] اسکریپت پایتون argv.py است و عناصر باقی مانده از لیست sys.argv شامل آرگومان های اسکریپت، ['un', 'deux', 'trois', 'quatre'] است.

به طور خلاصه، sys.argv شامل تمام آرگومان های خط فرمان پایتون argv.py است. هنگامی که مفسر پایتون یک برنامه پایتون را اجرا می کند، خط فرمان را تجزیه می کند و sys.argv را با آرگومان ها پر می کند.

معکوس کردن آرگومان اول

اکنون که پیشینه کافی در sys.argv دارید، می خواهید بر روی آرگومان های ارسال شده در خط فرمان کار کنید. مثال reverse.py اولین آرگومان ارسال شده در خط فرمان را معکوس می کند:

reverse.py

import sys

arg = sys.argv[1]
print(arg[::-1])

در reverse.py فرآیند معکوس کردن آرگومان اول با مراحل زیر انجام می شود:

  • خط 5 اولین آرگومان برنامه ذخیره شده در شاخص 1 sys.argv را واکشی می کند. به یاد داشته باشید که نام برنامه در ایندکس 0 sys.argv ذخیره می شود.
  • خط 6 رشته معکوس را چاپ می کند. args[::-1] یک روش پایتونی برای استفاده از عملیات اسلایس برای معکوس کردن یک لیست است.

شما اسکریپت را به صورت زیر اجرا می کنید:

python reverse.py "Real Python"
nohtyP laeR

همانطور که انتظار می رفت، reverse.py بر روی "Real Python" عمل می کند و تنها آرگومان را برای خروجی "nohtyP laeR" معکوس می کند. توجه داشته باشید که احاطه کردن رشته چند کلمه ای "Real Python" با نقل قول تضمین می کند که مفسر آن را به جای دو آرگومان به عنوان یک آرگومان منحصر به فرد مدیریت می کند. در بخش بعدی به جداکننده های آرگومان خواهید پرداخت.

جهش sys.argv

sys.argv به طور جهانی برای برنامه پایتون در حال اجرا شما در دسترس است. تمام ماژول های وارد شده در طول اجرای فرآیند دسترسی مستقیم به sys.argv دارند. این دسترسی جهانی ممکن است راحت باشد، اما sys.argv تغییرناپذیر نیست. ممکن است بخواهید مکانیسم قابل اعتمادتری را برای قرار دادن آرگومان های برنامه در معرض ماژول های مختلف در برنامه پایتون خود، به ویژه در یک برنامه پیچیده با چندین فایل پیاده سازی کنید.

مشاهده کنید که اگر sys.argv را دستکاری کنید چه اتفاقی می افتد:

argv_pop.py

import sys

print(sys.argv)
sys.argv.pop()
print(sys.argv)

شما .pop() را فراخوانی می کنید تا آخرین مورد را در sys.argv حذف و برگردانید.

اسکریپت بالا را اجرا کنید:

python argv_pop.py un deux trois quatre
['argv_pop.py', 'un', 'deux', 'trois', 'quatre']
['argv_pop.py', 'un', 'deux', 'trois']

توجه داشته باشید که آرگومان چهارم دیگر در sys.argv گنجانده نشده است.

در یک اسکریپت کوتاه، می توانید با خیال راحت به دسترسی سراسری به sys.argv تکیه کنید، اما در یک برنامه بزرگتر، ممکن است بخواهید آرگومان ها را در یک متغیر جداگانه ذخیره کنید. مثال قبلی را می توان به صورت زیر اصلاح کرد:

argv_var_pop.py

import sys

print(sys.argv)
args = sys.argv[1:]
print(args)
sys.argv.pop()
print(sys.argv)
print(args)

این بار، اگرچه sys.argv آخرین عنصر خود را از دست داد، args به طور ایمن حفظ شده است. ARGS جهانی نیست و می توانید آن را برای تجزیه آرگومان ها بر اساس منطق برنامه خود منتقل کنید. مدیر بسته پایتون، pip، از این رویکرد استفاده می کند. در اینجا گزیده کوتاهی از کد منبع pip آورده شده است:

def main(args=None):
    if args is None:
        args = sys.argv[1:]

در این قطعه کد برگرفته از کد منبع pip، main() تکه ای از sys.argv را که فقط شامل آرگومان ها است و نه نام فایل را در args ذخیره می کند. sys.argv دست نخورده باقی می ماند و args تحت تأثیر هیچ تغییر سهوی در sys.argv قرار نمی گیرد.

فرار از کاراکترهای فضای سفید

در reverse.py مثالی که قبلا دیدید، اولین و تنها آرگومان "Real Python" است و نتیجه "nohtyP laeR" است. این آرگومان شامل یک جداکننده فضای خالی بین "واقعی" و "پایتون" است و باید از آن فرار کرد.

در لینوکس، فضاهای خالی را می توان با انجام یکی از موارد زیر فرار کرد:

  1. احاطه آرگومان ها با نقل قول های تکی (')
  2. احاطه کردن استدلال ها با نقل قول های دوتایی (")
  3. پیشوند هر فاصله با یک اسلش معکوس (\)

بدون یکی از راه حل های فرار، reverse.py دو آرگومان را ذخیره می کند، "واقعی" در sys.argv[1] و "پایتون" در sys.argv[2]:

python reverse.py Real Python
laeR

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

همچنین می توانید از یک اسلش معکوس (\) برای فرار از فضای خالی استفاده کنید:

python reverse.py Real\ Python
nohtyP laeR

با اسلش معکوس (\)، پوسته فرمان یک آرگومان منحصر به فرد را در معرض پایتون و سپس به reverse.py قرار می دهد.

در پوسته های یونیکس، جداکننده میدان داخلی (IFS) کاراکترهایی را که به عنوان جداکننده استفاده می شوند، تعریف می کند. محتوای متغیر پوسته، IFS، را می توان با اجرای دستور زیر نمایش داد:

printf "%q\n" "$IFS"
$' \t\n'

از نتیجه بالا، '\t\n'، سه تعیین کننده را شناسایی می کنید:

  1. فضا (' ')
  2. برگه (\t)
  3. خط جدید (\n)

پیشوند یک فاصله با اسلش معکوس (\) رفتار پیش فرض فضا را به عنوان یک جداکننده در رشته "Real Python" دور می زند. این منجر به ایجاد یک بلوک متن همانطور که در نظر گرفته شده است، به جای دو.

توجه داشته باشید که در ویندوز، تفسیر فضای خالی را می توان با استفاده از ترکیبی از نقل قول های دوتایی مدیریت کرد. این کمی غیرمنطقی است زیرا در ترمینال ویندوز، یک نقل قول دوتایی (") به عنوان سوئیچ برای غیرفعال کردن و متعاقبا فعال کردن کاراکترهای خاص مانند space، tab یا pipe (|) تفسیر می شود.

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

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

C:/>python reverse.py "Real Python"
nohtyP laeR

در مثال بالا، می توانید به طور شهودی استنباط کنید که "پایتون واقعی" به عنوان یک آرگومان واحد تفسیر می شود. با این حال، بدانید که هنگام استفاده از یک نقل قول دوگانه چه اتفاقی می افتد:

C:/>python reverse.py "Real Python
nohtyP laeR

خط فرمان کل رشته "Real Python" را به عنوان یک آرگومان واحد ارسال می کند، به همان روشی که گویی آرگومان "Real Python" است. در واقع، خط فرمان ویندوز نقل قول دوگانه منحصر به فرد را به عنوان سوئیچ برای غیرفعال کردن رفتار فضاهای سفید به عنوان جداکننده می بیند و هر چیزی را که پس از نقل قول دوتایی به عنوان یک آرگومان منحصر به فرد ارسال می کند.

برای کسب اطلاعات بیشتر در مورد اثرات نقل قول های دوتایی در ترمینال ویندوز، یک راه بهتر برای درک نقل قول و فرار از آرگومان های خط فرمان ویندوز را بررسی کنید.

مدیریت خطاها

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

python reverse.py
Traceback (most recent call last):
  File "reverse.py", line 5, in <module>
    arg = sys.argv[1]
IndexError: list index out of range

استثنای پایتون IndexError مطرح شده است و traceback مربوطه نشان می دهد که خطا ناشی از عبارت arg=sys.argv[1] است. پیام استثنا فهرست فهرست خارج از محدوده است. شما آرگومانی را در خط فرمان ارسال نکردید، بنابراین چیزی در لیست sys.argv در فهرست 1 وجود ندارد.

این یک الگوی رایج است که می تواند به چند روش مختلف مورد توجه قرار گیرد. برای این مثال اولیه، با گنجاندن عبارت arg=sys.argv[1] در یک بلوک try، آن را مختصر نگه می دارید. کد را به صورت زیر تغییر دهید:

reverse_exc.py

import sys

try:
    arg = sys.argv[1]
except IndexError:
    raise SystemExit(f"Usage: {sys.argv[0]} <string_to_reverse>")
print(arg[::-1])

عبارت در خط 4 در یک بلوک try گنجانده شده است. خط 8 استثنای داخلی SystemExit را افزایش می دهد. اگر هیچ آرگومانی به reverse_exc.py منتقل نشود، پس از چاپ استفاده، فرآیند با کد وضعیت 1 خارج می شود. به ادغام sys.argv[0] در پیام خطا توجه کنید. نام برنامه را در پیام استفاده نشان می دهد. اکنون ، هنگامی که همان برنامه را بدون هیچ آرگومان خط فرمان پایتون اجرا می کنید ، می توانید خروجی زیر را مشاهده کنید:

python reverse.py
Usage: reverse.py <string_to_reverse>

echo $?
1

reverse.py آرگومانی در خط فرمان ارسال نکرد. در نتیجه، برنامه SystemExit را با یک پیام خطا افزایش می دهد. این باعث می شود که برنامه با وضعیت 1 خارج شود، که هنگام چاپ متغیر خاص با echo نمایش داده می شود.

محاسبه sha1sum

شما اسکریپت دیگری می نویسید تا نشان دهید که در سیستم های شبه یونیکس، آرگومان های خط فرمان پایتون توسط بایت های سیستم عامل منتقل می شوند. این اسکریپت یک رشته را به عنوان آرگومان می گیرد و هش SHA-1 هگزادسیمال آرگومان را خروجی می دهد:

sha1sum.py

import sys
import hashlib

data = sys.argv[1]
m = hashlib.sha1()
m.update(bytes(data, 'utf-8'))
print(m.hexdigest())

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

  • خط 6 محتوای آرگومان اول را در داده ها ذخیره می کند.
  • خط 7 یک الگوریتم SHA1 را نمونه سازی می کند.
  • خط 8 شی هش SHA1 را با محتوای اولین آرگومان برنامه به روز می کند. توجه داشته باشید که hash.update یک آرایه بایت را به عنوان آرگومان می گیرد، بنابراین لازم است داده ها را از یک رشته به آرایه بایت تبدیل کنید.
  • خط 9 یک نمایش هگزادسیمال از هش SHA1 محاسبه شده در خط 8 را چاپ می کند.

هنگامی که اسکریپت را با یک آرگومان اجرا می کنید، این را دریافت می کنید:

python sha1sum.py "Real Python"
0554943d034f044c5998f55dac8ee2c03e387565

برای کوتاه نگه داشتن مثال، اسکریپت sha1sum.py آرگومان های خط فرمان از دست رفته پایتون را مدیریت نمی کند. مدیریت خطا را می توان در این اسکریپت به همان روشی که در reverse_exc.py انجام دادید برطرف کرد.

از مستندات sys.argv، یاد می گیرید که برای دریافت بایت های اصلی آرگومان های خط فرمان پایتون، می توانید از os.fsencode() استفاده کنید. با به دست آوردن مستقیم بایت ها از sys.argv[1]، نیازی به انجام تبدیل رشته به بایت داده ها ندارید:

sha1sum_bytes.py

import os
import sys
import hashlib

data = os.fsencode(sys.argv[1])
m = hashlib.sha1()
m.update(data)
print(m.hexdigest())

تفاوت اصلی بین sha1sum.py و sha1sum_bytes.py در خطوط زیر برجسته شده است:

  • خط 7 داده ها را با بایت های اصلی ارسال شده به آرگومان های خط فرمان پایتون پر می کند.
  • خط 9 داده ها را به عنوان آرگومان به m.update() منتقل می کند، که یک شی بایت مانند دریافت می کند.

sha1sum_bytes.py را برای مقایسه خروجی اجرا کنید:

python sha1sum_bytes.py "Real Python"
0554943d034f044c5998f55dac8ee2c03e387565

مقدار هگزادسیمال هش SHA1 مانند مثال sha1sum.py قبلی است.

آناتومی آرگومان های خط فرمان پایتون

اکنون که چند جنبه از آرگومان های خط فرمان پایتون، به ویژه sys.argv را بررسی کرده اید، می خواهید برخی از استانداردهایی را که به طور منظم توسط توسعه دهندگان در حین پیاده سازی رابط خط فرمان استفاده می شود، اعمال کنید.

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

  1. گزینه ها رفتار یک دستور یا برنامه خاص را تغییر می دهند.
  2. آرگومان ها منبع یا مقصدی را نشان می دهند که باید پردازش شود.
  3. دستورات فرعی به یک برنامه اجازه می دهد تا بیش از یک دستور را با مجموعه ای از گزینه ها و آرگومان های مربوطه تعریف کند.

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

استانداردهای

چند استاندارد موجود برخی از تعاریف و دستورالعمل ها را برای ارتقای سازگاری برای اجرای دستورات و استدلال های آنها ارائه می دهند. اینها استانداردها و مراجع اصلی یونیکس هستند:

  • قراردادهای سودمندی POSIX
  • استانداردهای گنو برای رابط های خط فرمان
  • دکاپت

استانداردهای بالا دستورالعمل ها و نامگذاری ها را برای هر چیزی که مربوط به برنامه ها و آرگومان های خط فرمان پایتون است تعریف می کند. نکات زیر نمونه هایی است که از آن منابع گرفته شده است:

  • POSIX:

    • یک برنامه یا ابزار با گزینه ها، گزینه های آرگومان و عملوندها دنبال می شود.
    • قبل از همه گزینه ها باید یک خط فاصله یا کاراکتر تعیین کننده منفی (-) باشد.
    • گزینه-آرگومان نباید اختیاری باشد.
  • گنو:

    • همه برنامه ها باید از دو گزینه استاندارد پشتیبانی کنند که عبارتند از --version و --help.
    • گزینه های با نام بلند معادل گزینه های تک حرفی به سبک یونیکس هستند. به عنوان مثال --debug و -d است.
  • docopt:

    • گزینه های کوتاه را می توان روی هم چید، به این معنی که -abc معادل -a -b -c است.
    • گزینه های طولانی می توانند دارای آرگومان هایی باشند که بعد از فاصله یا علامت مساوی (=) مشخص شده اند. گزینه طولانی --input=ARG معادل --input ARG است.

این استانداردها نمادهایی را تعریف میکنند که هنگام توصیف یک دستور مفید هستند. از نماد مشابهی می توان برای نمایش استفاده از یک دستور خاص هنگام فراخوانی آن با گزینه -h یا --help استفاده کرد.

استانداردهای گنو بسیار شبیه به استانداردهای POSIX هستند اما برخی تغییرات و بسط ها را ارائه می دهند. قابل ذکر است که آنها گزینه طولانی را اضافه می کنند که یک گزینه کاملا نامگذاری شده با پیشوند دو خط تیره (--) است. به عنوان مثال، برای نمایش راهنما، گزینه معمولی -h و گزینه طولانی --help است.

در بخش های بعدی، در مورد هر یک از اجزای خط فرمان، گزینه ها، آرگومان ها و زیر فرمان ها بیشتر خواهید آموخت.

گزینه

گزینه ای که گاهی اوقات پرچم یا سوئیچ نامیده می شود، برای تغییر رفتار برنامه در نظر گرفته شده است. به عنوان مثال، دستور ls در لینوکس محتوای یک دایرکتوری معین را فهرست می کند. بدون هیچ آرگومانی، فایل ها و دایرکتوری ها را در دایرکتوری فعلی فهرست می کند:

cd /dev
ls
autofs
block
bsg
btrfs-control
bus
char
console

بیایید چند گزینه اضافه کنیم. می توانید -l و -s را در -ls ترکیب کنید، که اطلاعات نمایش داده شده در ترمینال را تغییر می دهد:

cd /dev
ls -ls
total 0
0 crw-r--r--  1 root root       10,   235 Jul 14 08:10 autofs
0 drwxr-xr-x  2 root root             260 Jul 14 08:10 block
0 drwxr-xr-x  2 root root              60 Jul 14 08:10 bsg
0 crw-------  1 root root       10,   234 Jul 14 08:10 btrfs-control
0 drwxr-xr-x  3 root root              60 Jul 14 08:10 bus
0 drwxr-xr-x  2 root root            4380 Jul 14 15:08 char
0 crw-------  1 root root        5,     1 Jul 14 08:10 console

یک گزینه می تواند یک آرگومان داشته باشد که به آن گزینه-آرگومان می گویند. نمونه ای را در عمل با od در زیر مشاهده کنید:

od -t x1z -N 16 main
0000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  >.ELF............<
0000020

OD مخفف Octal Dump است. این ابزار داده ها را در نمایش های مختلف قابل چاپ مانند هشت ضلعی (که پیش فرض است)، هگزادسیمال، اعشاری و ASCII نمایش می دهد. در مثال بالا، فایل باینری اصلی را می گیرد و 16 بایت اول فایل را با فرمت هگزادسیمال نمایش می دهد. گزینه -t انتظار یک نوع را به عنوان یک گزینه-آرگومان و -N تعداد بایت های ورودی را انتظار دارد.

در مثال بالا ، -t نوع x1 داده شده است که مخفف هگزادسیمال و یک بایت در هر عدد صحیح است. به دنبال آن z برای نمایش کاراکترهای قابل چاپ در انتهای خط ورودی است. -N 16 را به عنوان یک گزینه آرگومان برای محدود کردن تعداد بایت های ورودی به 16 در نظر می گیرد.

استدلال

آرگومان ها در استانداردهای POSIX عملوند یا پارامتر نیز نامیده می شوند. آرگومان ها منبع یا مقصد داده هایی را نشان می دهند که دستور روی آنها عمل می کند. به عنوان مثال، دستور cp که برای کپی کردن یک یا چند فایل در یک فایل یا یک دایرکتوری استفاده می شود، حداقل یک منبع و یک هدف را می گیرد:

ls main
main

cp main main2

ls -lt
main
main2
...

در خط 4، cp دو آرگومان می گیرد:

  1. اصلی: فایل منبع
  2. main2: فایل هدف

سپس محتوای اصلی را در یک فایل جدید به نام main2 کپی می کند. هر دو اصلی و اصلی2 آرگومان ها یا عملوندهای برنامه cp هستند.

دستورات فرعی

مفهوم دستورات فرعی در استانداردهای POSIX یا GNU مستند نشده است، اما در docopt ظاهر می شود. ابزارهای استاندارد یونیکس ابزارهای کوچکی هستند که به فلسفه یونیکس پایبند هستند. برنامه های یونیکس برنامه هایی هستند که یک کار را انجام می دهند و آن را به خوبی انجام می دهند. این بدان معناست که هیچ دستوری فرعی لازم نیست.

در مقابل، نسل جدیدی از برنامه ها، از جمله git، go، docker و gcloud، با پارادایم کمی متفاوت ارائه می شوند که دستورات فرعی را در بر می گیرد. آنها لزوما بخشی از چشم انداز یونیکس نیستند زیرا چندین سیستم عامل را در بر می گیرند و با یک اکوسیستم کامل مستقر شده اند که به چندین دستور نیاز دارد.

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

  • برنچ گیت برنچ های مخزن محلی گیت را نمایش می دهد.
  • برنچ گیت custom_python یک برنچ محلی custom_python در یک مخزن محلی ایجاد می کند.
  • git branch -d custom_python custom_python شاخه محلی را حذف می کند.
  • git branch --help راهنما را برای دستور فرعی شاخه git نمایش می دهد.

در اکوسیستم پایتون، pip مفهوم دستورات فرعی را نیز دارد. برخی از دستورات فرعی pip شامل list، install، freeze یا uninstall هستند.

Windows

در ویندوز، قراردادهای مربوط به آرگومان های خط فرمان پایتون کمی متفاوت است، به ویژه، قراردادهای مربوط به گزینه های خط فرمان. برای تأیید این تفاوت، Tasklist را در نظر بگیرید، که یک فایل اجرایی بومی ویندوز است که لیستی از فرآیندهای در حال اجرا را نمایش می دهد. این شبیه به ps در سیستم های لینوکس یا macOS است. در زیر نمونه ای از نحوه اجرای لیست وظایف در خط فرمان در ویندوز آورده شده است:

C:/>tasklist /FI "IMAGENAME eq notepad.exe"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
notepad.exe                  13104 Console                    6     13,548 K
notepad.exe                   6584 Console                    6     13,696 K

توجه داشته باشید که جداکننده یک گزینه به جای خط فاصله (-) مانند قراردادهای سیستم های یونیکس، یک اسلش رو به جلو (/) است. برای خوانایی، فاصله ای بین نام برنامه، لیست وظایف و گزینه /FI وجود دارد، اما تایپ کردن taskslist/FI به همان اندازه صحیح است.

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

ps -ef | grep vi | grep -v grep
andre     2117     4  0 13:33 tty1     00:00:00 vi .gitignore
andre     2163  2134  0 13:34 tty3     00:00:00 vi main.c

دستور ps در بالا تمام فرآیندهای فعلی vi را نشان می دهد. این رفتار با فلسفه یونیکس سازگار است، زیرا خروجی ps توسط دو فیلتر grep تبدیل می شود. دستور grep اول تمام رخدادهای vi را انتخاب می کند و grep دوم وقوع grep را فیلتر می کند.

با گسترش ابزارهای یونیکس که در اکوسیستم ویندوز ظاهر می شوند، قراردادهای غیر خاص ویندوز نیز در ویندوز پذیرفته می شوند.

تصاویری

در شروع فرآیند پایتون، آرگومان های خط فرمان پایتون به دو دسته تقسیم می شوند:

  1. گزینه های پایتون: اینها بر اجرای مفسر پایتون تأثیر می گذارند. به عنوان مثال، افزودن گزینه -O وسیله ای برای بهینه سازی اجرای یک برنامه پایتون با حذف دستورات assert و __debug__ است. گزینه های پایتون دیگری نیز در خط فرمان موجود است.

  2. برنامه پایتون و آرگومان های آن: با دنبال کردن گزینه های پایتون (در صورت وجود)، برنامه پایتون را پیدا خواهید کرد که نام فایلی است که معمولا دارای پسوند .py و آرگومان های آن است. طبق قرارداد، آنها همچنین می توانند از گزینه ها و استدلال ها تشکیل شوند.

دستور زیر را که برای اجرای برنامه در نظر گرفته شده است main.py بگیرید، که گزینه ها و آرگومان ها را می گیرد. توجه داشته باشید که در این مثال، مفسر پایتون گزینه هایی را نیز در نظر می گیرد که عبارتند از -B و -v.

python -B -v main.py --verbose --debug un deux

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

  • گزینه -B به پایتون می گوید که فایل های .pyc را در وارد کردن ماژول های منبع ننویسد. برای جزئیات بیشتر در مورد فایل های pyc، بخش کامپایلر چه کاری انجام می دهد؟ را در راهنمای شما برای کد منبع CPython بررسی کنید.
  • گزینه -v مخفف verbose است و به پایتون می گوید که تمام دستورات واردات را ردیابی کند.
  • آرگومان هایی که به main.py منتقل می شوند ساختگی هستند و دو گزینه طولانی (--verbose و --debug) و دو آرگومان (un و deux) را نشان می دهند.

این مثال از آرگومان های خط فرمان پایتون را می توان به صورت گرافیکی به صورت زیرین نشان داد:

در main.py برنامه پایتون، شما فقط به آرگومان های خط فرمان پایتون که توسط پایتون در sys.argv درج شده است دسترسی دارید. گزینه های پایتون ممکن است بر رفتار برنامه تأثیر بگذارند اما در main.py قابل دسترسی نیستند.

چند روش برای تجزیه آرگومان های خط فرمان پایتون

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

  • استفاده از عبارات منظم برای استخراج عناصر خط فرمان
  • یادگیری نحوه مدیریت فایل های ارسال شده در خط فرمان
  • ورودی استاندارد را به گونه ای درک کنید که با ابزارهای یونیکس سازگار باشد
  • خروجی منظم برنامه را از خطاها متمایز کنید
  • پیاده سازی یک تجزیه کننده سفارشی برای خواندن آرگومان های خط فرمان پایتون

این به عنوان آماده سازی برای گزینه های مربوط به ماژول ها در کتابخانه های استاندارد یا از کتابخانه های خارجی است که بعدا در این آموزش با آنها آشنا خواهید شد.

برای چیزی بدون پیچیده، الگوی زیر، که ترتیب را اجرا نمی کند و گزینه آرگومان ها را مدیریت نمی کند، ممکن است کافی باشد:

cul.py

import sys

opts = [opt for opt in sys.argv[1:] if opt.startswith("-")]
args = [arg for arg in sys.argv[1:] if not arg.startswith("-")]

if "-c" in opts:
    print(" ".join(arg.capitalize() for arg in args))
elif "-u" in opts:
    print(" ".join(arg.upper() for arg in args))
elif "-l" in opts:
    print(" ".join(arg.lower() for arg in args))
else:
    raise SystemExit(f"Usage: {sys.argv[0]} (-c | -u | -l) <arguments>...")

هدف برنامه بالا تغییر مورد آرگومان های خط فرمان پایتون است. سه گزینه در دسترس است:

  • -c برای بزرگ کردن آرگومان ها
  • -u برای تبدیل آرگومان ها به حروف بزرگ
  • -l برای تبدیل آرگومان به حروف کوچک

کد انواع آرگومان های مختلف را با استفاده از درک لیست جمع آوری و جدا می کند:

  • خط 5 تمام گزینه ها را با فیلتر کردن بر روی هر آرگومان خط فرمان پایتون که با خط فاصله (-) شروع می شود، جمع آوری می کند.
  • خط 6 آرگومان های برنامه را با فیلتر کردن گزینه ها جمع آوری می کند.

هنگامی که برنامه پایتون را در بالا با مجموعه ای از گزینه ها و آرگومان ها اجرا می کنید، خروجی زیر را دریافت می کنید:

python cul.py -c un deux trois
Un Deux Trois

این رویکرد ممکن است در بسیاری از شرایط کافی باشد، اما در موارد زیر شکست می خورد:

  • اگر ترتیب مهم است، و به ویژه، اگر گزینه ها باید قبل از آرگومان ها ظاهر شوند
  • اگر پشتیبانی از گزینه-آرگومان ها مورد نیاز باشد
  • اگر برخی از آرگومان ها با خط فاصله (-) پیشوند شده باشند

قبل از اینکه به کتابخانه ای مانند argparse یا کلیک کنید، می توانید از گزینه های دیگر استفاده کنید.

عبارات منظم

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

Print integers from <first> to <last>, in steps of <increment>.

Usage:
  python seq.py --help
  python seq.py [-s SEPARATOR] <last>
  python seq.py [-s SEPARATOR] <first> <last>
  python seq.py [-s SEPARATOR] <first> <increment> <last>

Mandatory arguments to long options are mandatory for short options too.
  -s, --separator=STRING use STRING to separate numbers (default: \n)
      --help             display this help and exit

If <first> or <increment> are omitted, they default to 1. When <first> is
larger than <last>, <increment>, if not set, defaults to -1.
The sequence of numbers ends when the sum of the current number and
<increment> reaches the limit imposed by <last>.

ابتدا، به یک عبارت منظم نگاه کنید که برای گرفتن الزامات بالا در نظر گرفته شده است:

args_pattern = re.compile(
    r"""
    ^
    (
        (--(?P<HELP>help).*)|
        ((?:-s|--separator)\s(?P<SEP>.*?)\s)?
        ((?P<OP1>-?\d+))(\s(?P<OP2>-?\d+))?(\s(?P<OP3>-?\d+))?
    )
    $
""",
    re.VERBOSE,
)

برای آزمایش عبارت منظم بالا، می توانید از قطعه ضبط شده در عبارت منظم 101 استفاده کنید. عبارت منظم چند جنبه از الزامات داده شده برای seq را به تصویر می کشد و اجرا می کند. به طور خاص، دستور ممکن است موارد زیر را بگیرد:

  1. یک گزینه راهنما، در قالب کوتاه (-h) یا طولانی (--help)، که به عنوان یک گروه نامگذاری شده به نام HELP ضبط شده است
  2. یک گزینه جداکننده، -s یا --separator، با گرفتن یک آرگومان اختیاری، و به عنوان گروه نامگذاری شده به نام SEP ضبط می شود
  3. حداکثر سه عملوند صحیح، به ترتیب به عنوان OP1، OP2 و OP3 ضبط شده اند.

برای وضوح، الگوی args_pattern بالا از پرچم re. VERBOSE در خط 11. این به شما امکان می دهد عبارت منظم را در چند خط پخش کنید تا خوانایی را افزایش دهید. این الگو موارد زیر را تأیید می کند:

  • ترتیب استدلال: انتظار می رود گزینه ها و آرگومان ها به ترتیب معین ارائه شوند. به عنوان مثال، گزینه ها قبل از آرگومان ها انتظار می رود.
  • مقادیر گزینه**: فقط --help، -s یا --separator به عنوان گزینه انتظار می رود.
  • انحصار متقابل استدلال: گزینه --help با گزینه ها یا آرگومان های دیگر سازگار نیست.
  • نوع آرگومان: انتظار می رود عملوندها اعداد صحیح مثبت یا منفی باشند.

برای اینکه عبارت منظم بتواند این موارد را مدیریت کند، باید تمام آرگومان های خط فرمان پایتون را در یک رشته ببیند. می توانید آنها را با استفاده از str.join() جمع آوری کنید:

arg_line = " ".join(sys.argv[1:])

این باعث می شود arg_line رشته ای باشد که شامل همه آرگومان ها، به جز نام برنامه، با یک فاصله از هم جدا شده باشد.

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

def parse(arg_line: str) -> Dict[str, str]:
    args: Dict[str, str] = {}
    if match_object := args_pattern.match(arg_line):
        args = {k: v for k, v in match_object.groupdict().items()
                if v is not None}
    return args

این الگو در حال حاضر ترتیب آرگومان ها، انحصار متقابل بین گزینه ها و آرگومان ها و نوع آرگومان ها را مدیریت می کند. parse() re.match() را به خط آرگومان اعمال می کند تا مقادیر مناسب را استخراج کند و داده ها را در یک فرهنگ لغت ذخیره کند.

فرهنگ لغت شامل نام هر گروه به عنوان کلید و مقادیر مربوط به آنها است. برای مثال، اگر مقدار arg_line --help باشد، فرهنگ لغت {'HELP': 'help'} است. اگر arg_line -s T 10 باشد، فرهنگ لغت تبدیل به {'SEP': 'T', 'OP1': '10'} می شود. می توانید بلوک کد زیر را گسترش دهید تا پیاده سازی seq با عبارات منظم را مشاهده کنید.

کد زیر یک نسخه محدود از seq را با یک عبارت منظم برای مدیریت تجزیه و اعتبارسنجی خط فرمان پیاده سازی می کند:

seq_regex.py

from typing import List, Dict
import re
import sys

USAGE = (
    f"Usage: {sys.argv[0]} [-s <separator>] [first [increment]] last"
)

args_pattern = re.compile(
    r"""
    ^
    (
        (--(?P<HELP>help).*)|
        ((?:-s|--separator)\s(?P<SEP>.*?)\s)?
        ((?P<OP1>-?\d+))(\s(?P<OP2>-?\d+))?(\s(?P<OP3>-?\d+))?
    )
    $
""",
    re.VERBOSE,
)

def parse(arg_line: str) -> Dict[str, str]:
    args: Dict[str, str] = {}
    if match_object := args_pattern.match(arg_line):
        args = {k: v for k, v in match_object.groupdict().items()
                if v is not None}
    return args

def seq(operands: List[int], sep: str = "\n") -> str:
    first, increment, last = 1, 1, 1
    if len(operands) == 1:
        last = operands[0]
    if len(operands) == 2:
        first, last = operands
        if first > last:
            increment = -1
    if len(operands) == 3:
        first, increment, last = operands
    last = last + 1 if increment > 0 else last - 1
    return sep.join(str(i) for i in range(first, last, increment))

def main() -> None:
    args = parse(" ".join(sys.argv[1:]))
    if not args:
        raise SystemExit(USAGE)
    if args.get("HELP"):
        print(USAGE)
        return
    operands = [int(v) for k, v in args.items() if k.startswith("OP")]
    sep = args.get("SEP", "\n")
    print(seq(operands, sep))

if __name__ == "__main__":
    main()

با اجرای این دستور می توانید کد بالا را اجرا کنید:

python seq_regex.py 3

این باید موارد زیر را خروجی دهد:

1
2
3

این دستور را با ترکیب های دیگر، از جمله گزینه --help امتحان کنید.

شما گزینه نسخه ای را در اینجا مشاهده نکردید. این کار عمدا برای کاهش طول مثال انجام شد. ممکن است گزینه نسخه را به عنوان یک تمرین گسترده اضافه کنید. به عنوان یک اشاره، می توانید عبارت منظم را با جایگزینی خط (--(؟ P<HELP>HELP).*)|با (--(؟ P<HELP>HELP).*)|(--(? P<VER>version).*)|. یک بلوک اگر اضافی نیز در main() مورد نیاز است.

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

مدیریت فایل

اکنون زمان آن رسیده است که آرگومان های خط فرمان پایتون را آزمایش کنیم که انتظار می رود نام فایل باشند. sha1sum.py را تغییر دهید تا یک یا چند فایل را به عنوان آرگومان مدیریت کنید. در نهایت یک نسخه کاهش یافته از ابزار اصلی sha1sum خواهید داشت که یک یا چند فایل را به عنوان آرگومان می گیرد و هش SHA1 هگزادسیمال را برای هر فایل نمایش می دهد و به دنبال آن نام فایل را نمایش می دهد:

sha1sum_file.py

import hashlib
import sys

def sha1sum(filename: str) -> str:
    hash = hashlib.sha1()
    with open(filename, mode="rb") as f:
        hash.update(f.read())
    return hash.hexdigest()

for arg in sys.argv[1:]:
    print(f"{sha1sum(arg)}  {arg}")

sha1sum() به جای خود رشته، روی داده های خوانده شده از هر فایلی که در خط فرمان ارسال کرده اید، اعمال می شود. توجه داشته باشید که m.update() یک شی بایت مانند را به عنوان آرگومان می گیرد و نتیجه فراخوانی read() پس از باز کردن یک فایل با حالت rb، یک شی بایت را برمی گرداند. برای اطلاعات بیشتر در مورد مدیریت محتوای فایل، خواندن و نوشتن فایل ها در پایتون و به طور خاص، بخش کار با بایت ها را بررسی کنید.

تکامل sha1sum_file.py از مدیریت رشته ها در خط فرمان تا دستکاری محتوای فایل ها، شما را به پیاده سازی اصلی sha1sum نزدیک می کند:

sha1sum main main.c
9a6f82c245f5980082dbf6faac47e5085083c07d  main
125a0f900ff6f164752600550879cbfabb098bc3  main.c

اجرای برنامه پایتون با همان آرگومان های خط فرمان پایتون این را نشان می دهد:

python sha1sum_file.py main main.c
9a6f82c245f5980082dbf6faac47e5085083c07d  main
125a0f900ff6f164752600550879cbfabb098bc3  main.c

از آنجایی که با مفسر پوسته یا خط فرمان ویندوز تعامل دارید، از گسترش عام ارائه شده توسط پوسته نیز بهره مند می شوید. برای اثبات این موضوع، می توانید از main.py استفاده مجدد کنید، که هر آرگومان را با شماره آرگومان و مقدار آن نمایش می دهد:

python main.py main.*
Arguments count: 5
Argument      0: main.py
Argument      1: main.c
Argument      2: main.exe
Argument      3: main.obj
Argument      4: main.py

می توانید ببینید که پوسته به طور خودکار گسترش حروف عام را انجام می دهد به طوری که هر فایلی با نام پایه مطابق با main، صرف نظر از پسوند، بخشی از sys.argv باشد.

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

main_win.py

import sys
import glob
import itertools
from typing import List

def expand_args(args: List[str]) -> List[str]:
    arguments = args[:1]
    glob_args = [glob.glob(arg) for arg in args[1:]]
    arguments += itertools.chain.from_iterable(glob_args)
    return arguments

if __name__ == "__main__":
    args = expand_args(sys.argv)
    print(f"Arguments count: {len(args)}")
    for i, arg in enumerate(args):
        print(f"Argument {i:>6}: {arg}")

در main_win.py، expand_args برای پردازش حروف عام به سبک پوسته به glob.glob() متکی است. می توانید نتیجه را در ویندوز و هر سیستم عامل دیگری تأیید کنید:

C:/>python main_win.py main.*
Arguments count: 5
Argument      0: main_win.py
Argument      1: main.c
Argument      2: main.exe
Argument      3: main.obj
Argument      4: main.py

این مشکل مدیریت فایل ها با استفاده از حروف عام مانند ستاره (*) یا علامت سوال (؟) را برطرف می کند، اما stdin چطور؟

اگر هیچ پارامتری را به ابزار اصلی sha1sum ارسال نکنید، انتظار دارد داده ها را از ورودی استاندارد بخواند. این متنی است که در ترمینال وارد می کنید و با تایپ Ctrl+D در سیستم های شبه یونیکس یا Ctrl+Z در ویندوز به پایان می رسد. این دنباله های کنترلی انتهای فایل (EOF) را به ترمینال ارسال می کنند که خواندن از stdin را متوقف می کند و داده های وارد شده را برمی گرداند.

در بخش بعدی، توانایی خواندن از جریان ورودی استاندارد را به کد خود اضافه خواهید کرد.

ورودی استاندارد

هنگامی که پیاده سازی قبلی پایتون sha1sum را برای مدیریت ورودی استاندارد با استفاده از sys.stdin تغییر می دهید، به sha1sum اصلی نزدیک تر می شوید:

sha1sum_stdin.py

from typing import List
import hashlib
import pathlib
import sys

def process_file(filename: str) -> bytes:
    return pathlib.Path(filename).read_bytes()

def process_stdin() -> bytes:
    return bytes("".join(sys.stdin), "utf-8")

def sha1sum(data: bytes) -> str:
    sha1_hash = hashlib.sha1()
    sha1_hash.update(data)
    return sha1_hash.hexdigest()

def output_sha1sum(data: bytes, filename: str = "-") -> None:
    print(f"{sha1sum(data)}  {filename}")

def main(args: List[str]) -> None:
    if not args:
        args = ["-"]
    for arg in args:
        if arg == "-":
            output_sha1sum(process_stdin(), "-")
        else:
            output_sha1sum(process_file(arg), arg)

if __name__ == "__main__":
    main(sys.argv[1:])

دو قرارداد برای این نسخه جدید sha1sum اعمال می شود:

  1. بدون هیچ آرگومانی، برنامه انتظار دارد که داده ها در ورودی استاندارد، sys.stdin، که یک شی فایل قابل خواندن است، ارائه شود.
  2. هنگامی که یک خط فاصله (-) به عنوان آرگومان فایل در خط فرمان ارائه می شود، برنامه آن را به عنوان خواندن فایل از ورودی استاندارد تفسیر می کند.

این اسکریپت جدید را بدون هیچ استدلالی امتحان کنید. اولین ضرب المثل ذن پایتون را وارد کنید، سپس ورودی را با میانبر صفحه کلید تکمیل کنید Ctrl+D در سیستم های شبه یونیکس یا Ctrl+Z در ویندوز:

python sha1sum_stdin.py
Beautiful is better than ugly.
ae5705a3efd4488dfc2b4b80df85f60c67d998c4  -

همچنین می توانید یکی از آرگومان ها را به صورت stdin مخلوط با سایر آرگومان های فایل مانند موارد زیر قرار دهید:

python sha1sum_stdin.py main.py - main.c
d84372fc77a90336b6bb7c5e959bcb1b24c608b4  main.py
Beautiful is better than ugly.
ae5705a3efd4488dfc2b4b80df85f60c67d998c4  -
125a0f900ff6f164752600550879cbfabb098bc3  main.c

رویکرد دیگر در سیستم های شبه یونیکس ارائه /dev/stdin به جای - برای مدیریت ورودی استاندارد است:

python sha1sum_stdin.py main.py /dev/stdin main.c
d84372fc77a90336b6bb7c5e959bcb1b24c608b4  main.py
Beautiful is better than ugly.
ae5705a3efd4488dfc2b4b80df85f60c67d998c4  /dev/stdin
125a0f900ff6f164752600550879cbfabb098bc3  main.c

در ویندوز هیچ معادلی برای /dev/stdin وجود ندارد، بنابراین استفاده از - به عنوان آرگومان فایل همانطور که انتظار می رود کار می کند.

sha1sum_stdin.py اسکریپت تمام مدیریت خطاهای لازم را پوشش نمی دهد، اما بعدا در این آموزش برخی از ویژگی های از دست رفته را پوشش خواهید داد.

خروجی استاندارد و خطای استاندارد

پردازش خط فرمان ممکن است رابطه مستقیمی با stdin داشته باشد تا به قراردادهای توضیح داده شده در بخش قبل احترام بگذارد. خروجی استاندارد، اگرچه بلافاصله مرتبط نیست، اما اگر می خواهید به فلسفه یونیکس پایبند باشید، همچنان نگران کننده است. برای اینکه برنامه های کوچک با هم ترکیب شوند، ممکن است مجبور شوید سه جریان استاندارد را در نظر بگیرید:

    stdin
    stdout
    stderr

خروجی یک برنامه به ورودی برنامه دیگری تبدیل می شود و به شما امکان می دهد تاسیسات کوچک را زنجیره ای کنید. به عنوان مثال، اگر می خواهید قصه های ذن پایتون را مرتب کنید، می توانید موارد زیر را اجرا کنید:

python -c "import this" | sort
Although never is often better than *right* now.
Although practicality beats purity.
Although that way may not be obvious at first unless you're Dutch.
...

خروجی بالا برای خوانایی بهتر کوتاه شده است. حالا تصور کنید که برنامه ای دارید که داده های یکسانی را خروجی می دهد اما برخی از اطلاعات اشکال زدایی را نیز چاپ می کند:

zen_sort_debug.py

print("DEBUG >>> About to print the Zen of Python")
import this
print("DEBUG >>> Done printing the Zen of Python")

اجرای اسکریپت پایتون در بالا به موارد زیر می دهد:

python zen_sort_debug.py
DEBUG >>> About to print the Zen of Python
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
...
DEBUG >>> Done printing the Zen of Python

بیضی (... ) نشان می دهد که خروجی برای بهبود خوانایی کوتاه شده است.

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

python zen_sort_debug.py | sort

Although never is often better than *right* now.
Although practicality beats purity.
Although that way may not be obvious at first unless you're Dutch.
Beautiful is better than ugly.
Complex is better than complicated.
DEBUG >>> About to print the Zen of Python
DEBUG >>> Done printing the Zen of Python
Errors should never pass silently.
...

ممکن است متوجه شوید که قصد نداشتید خروجی اشکال زدایی را به عنوان ورودی دستور مرتب سازی داشته باشید. برای رفع این مشکل، می خواهید به جای آن، ردیابی ها را به جریان خطاهای استاندارد، stderr ارسال کنید:

zen_sort_stderr.py
import sys

print("DEBUG >>> About to print the Zen of Python", file=sys.stderr)
import this
print("DEBUG >>> Done printing the Zen of Python", file=sys.stderr)

برای رعایت موارد زیر zen_sort_stderr.py اجرا کنید:

python zen_sort_stderr.py | sort
DEBUG >>> About to print the Zen of Python
DEBUG >>> Done printing the Zen of Python

Although never is often better than *right* now.
Although practicality beats purity.
Although that way may not be obvious at first unless you're Dutch
....

اکنون، ردپاها در ترمینال نمایش داده می شوند، اما به عنوان ورودی دستور مرتب سازی استفاده نمی شوند.

تجزیه کننده های سفارشی

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

یک پیاده سازی ممکن برای پردازش استدلال های seq_parse.py می تواند به شرح زیر باشد:

def parse(args: List[str]) -> Tuple[str, List[int]]:
    arguments = collections.deque(args)
    separator = "\n"
    operands: List[int] = []
    while arguments:
        arg = arguments.popleft()
        if not operands:
            if arg == "--help":
                print(USAGE)
                sys.exit(0)
            if arg in ("-s", "--separator"):
                separator = arguments.popleft()
                continue
        try:
            operands.append(int(arg))
        except ValueError:
            raise SystemExit(USAGE)
        if len(operands) > 3:
            raise SystemExit(USAGE)

    return separator, operands

parse() لیستی از آرگومان ها بدون نام فایل پایتون داده می شود و از collections.deque() برای بهره مندی از .popleft() استفاده می کند که عناصر را از سمت چپ مجموعه حذف می کند. همانطور که آیتم های لیست آرگومان ها آشکار می شوند، منطقی را که برای برنامه شما انتظار می رود اعمال می کنید. در parse() می توانید موارد زیر را مشاهده کنید:

  • حلقه while در هسته تابع قرار دارد و زمانی خاتمه می یابد که دیگر آرگومانی برای تجزیه وجود نداشته باشد، زمانی که کمک فراخوانی می شود، یا زمانی که خطایی رخ می دهد.
  • اگر گزینه جداکننده شناسایی شود، انتظار می رود آرگومان بعدی جداکننده باشد.
  • عملوندها اعداد صحیحی را که برای محاسبه دنباله استفاده می شوند ذخیره می کنند. باید حداقل یک عملوند و حداکثر سه عملوند وجود داشته باشد.

نسخه کامل کد parse() در زیر موجود است:

seq_parse.py

from typing import Dict, List, Tuple
import collections
import re
import sys

USAGE = (f"Usage: {sys.argv[0]} "
         "[--help] | [-s <sep>] [first [incr]] last")

def seq(operands: List[int], sep: str = "\n") -> str:
    first, increment, last = 1, 1, 1
    if len(operands) == 1:
        last = operands[0]
    if len(operands) == 2:
        first, last = operands
        if first > last:
            increment = -1
    if len(operands) == 3:
        first, increment, last = operands
    last = last + 1 if increment > 0 else last - 1
    return sep.join(str(i) for i in range(first, last, increment))

def parse(args: List[str]) -> Tuple[str, List[int]]:
    arguments = collections.deque(args)
    separator = "\n"
    operands: List[int] = []
    while arguments:
        arg = arguments.popleft()
        if not len(operands):
            if arg == "--help":
                print(USAGE)
                sys.exit(0)
            if arg in ("-s", "--separator"):
                separator = arguments.popleft() if arguments else None
                continue
        try:
            operands.append(int(arg))
        except ValueError:
            raise SystemExit(USAGE)
        if len(operands) > 3:
            raise SystemExit(USAGE)

    return separator, operands

def main() -> None:
    sep, operands = parse(sys.argv[1:])
    if not operands:
        raise SystemExit(USAGE)
    print(seq(operands, sep))

if __name__ == "__main__":
    main()

توجه داشته باشید که برخی از جنبه های مدیریت خطا به حداقل می رسند تا مثال ها نسبتا کوتاه بمانند.

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

  • تعداد زیادی آرگومان
  • پیچیدگی و وابستگی متقابل بین آرگومان ها
  • اعتبارسنجی برای انجام در برابر آرگومان ها

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

چند روش برای اعتبارسنجی آرگومان های خط فرمان پایتون

شما قبلا اعتبارسنجی آرگومان های خط فرمان پایتون را در چند مثال مانند seq_regex.py و seq_parse.py انجام داده اید. در مثال اول، شما از یک عبارت منظم و در مثال دوم، از یک تجزیه کننده سفارشی استفاده کردید.

هر دوی این نمونه ها جنبه های یکسانی را در نظر گرفتند. آنها گزینه های مورد انتظار را به صورت کوتاه (-s) یا بلند (--separator) در نظر گرفتند. آنها ترتیب استدلال ها را در نظر گرفتند تا گزینه ها بعد از عملوندها قرار نگیرند. سرانجام ، آنها نوع ، عدد صحیح عملوندها و تعداد آرگومان ها را از یک تا سه آرگومان در نظر گرفتند.

اعتبارسنجی تایپ با کلاس های داده پایتون

در زیر اثبات مفهومی است که تلاش می کند نوع آرگومان های ارسال شده در خط فرمان را تأیید کند. در مثال زیر، تعداد آرگومان ها و نوع مربوطه آنها را تأیید می کنید:

val_type_dc.py

import dataclasses
import sys
from typing import List, Any

USAGE = f"Usage: python {sys.argv[0]} [--help] | firstname lastname age]"

@dataclasses.dataclass
class Arguments:
    firstname: str
    lastname: str
    age: int = 0

def check_type(obj):
    for field in dataclasses.fields(obj):
        value = getattr(obj, field.name)
        print(
            f"Value: {value}, "
            f"Expected type {field.type} for {field.name}, "
            f"got {type(value)}"
        )
        if type(value) != field.type:
            print("Type Error")
        else:
            print("Type Ok")

def validate(args: List[str]):
    # If passed to the command line, need to convert
    # the optional 3rd argument from string to int
    if len(args) > 2 and args[2].isdigit():
        args[2] = int(args[2])
    try:
        arguments = Arguments(*args)
    except TypeError:
        raise SystemExit(USAGE)
    check_type(arguments)

def main() -> None:
    args = sys.argv[1:]
    if not args:
        raise SystemExit(USAGE)

    if args[0] == "--help":
        print(USAGE)
    else:
        validate(args)

if __name__ == "__main__":
    main()

مگر اینکه گزینه --help را در خط فرمان ارسال کنید، این اسکریپت انتظار دو یا سه آرگومان را دارد:

  1. یک رشته اجباری: نام
  2. یک رشته اجباری: نام خانوادگی
  3. یک عدد صحیح اختیاری: سن

از آنجایی که تمام آیتم های sys.argv رشته هستند، اگر آرگومان سوم اختیاری از ارقام تشکیل شده باشد، باید آرگومان سوم اختیاری را به یک عدد صحیح تبدیل کنید. str.isdigit() تأیید می کند که آیا همه کاراکترهای یک رشته رقم هستند. علاوه بر این، با ساخت آرگومان های کلاس داده با مقادیر آرگومان های تبدیل شده، دو اعتبار سنجی به دست می آورید:

  1. اگر تعداد آرگومان ها با تعداد فیلدهای اجباری مورد انتظار آرگومان ها مطابقت نداشته باشد، با خطا مواجه می شوید. این حداقل دو و حداکثر سه فیلد است.
  2. اگر انواع پس از تبدیل با انواع تعریف شده در تعریف کلاس داده آرگومان ها مطابقت نداشته باشند، با خطا مواجه می شوید.

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

python val_type_dc.py Guido "Van Rossum" 25
Value: Guido, Expected type <class 'str'> for firstname, got <class 'str'>
Type Ok
Value: Van Rossum, Expected type <class 'str'> for lastname, got <class 'str'>
Type Ok
Value: 25, Expected type <class 'int'> for age, got <class 'int'>
Type Ok

در اجرای بالا، تعداد آرگومان ها صحیح است و نوع هر آرگومان نیز صحیح است.

اکنون، همان دستور را اجرا کنید اما آرگومان سوم را حذف کنید:

python val_type_dc.py Guido "Van Rossum"
Value: Guido, Expected type <class 'str'> for firstname, got <class 'str'>
Type Ok
Value: Van Rossum, Expected type <class 'str'> for lastname, got <class 'str'>
Type Ok
Value: 0, Expected type <class 'int'> for age, got <class 'int'>
Type Ok

نتیجه نیز موفقیت آمیز است زیرا سن فیلد با یک مقدار پیش فرض، 0 تعریف شده است، بنابراین کلاس داده Arguments به آن نیاز ندارد.

برعکس، اگر آرگومان سوم از نوع مناسب نباشد - مثلا یک رشته به جای عدد صحیح - آنگاه یک خطا دریافت می کنید:

python val_type_dc.py Guido Van Rossum
Value: Guido, Expected type <class 'str'> for firstname, got <class 'str'>
Type Ok
Value: Van, Expected type <class 'str'> for lastname, got <class 'str'>
Type Ok
Value: Rossum, Expected type <class 'int'> for age, got <class 'str'>
Type Error

مقدار مورد انتظار Van Rossum، با مظنه ها احاطه نشده است، بنابراین تقسیم شده است. کلمه دوم نام خانوادگی، Rossum، رشته ای است که به عنوان سن مدیریت می شود، که انتظار می رود یک int باشد. اعتبارسنجی با شکست مواجه می شود.

به طور مشابه، می توانید از NamedTuple برای دستیابی به اعتبارسنجی مشابه نیز استفاده کنید. شما کلاس داده را با یک کلاس مشتق شده از NamedTuple جایگزین می کنید و check_type() به صورت زیر تغییر می کند:

from typing import NamedTuple

class Arguments(NamedTuple):
    firstname: str
    lastname: str
    age: int = 0

def check_type(obj):
    for attr, value in obj._asdict().items():
        print(
            f"Value: {value}, "
            f"Expected type {obj.__annotations__[attr]} for {attr}, "
            f"got {type(value)}"
        )
        if type(value) != obj.__annotations__[attr]:
            print("Type Error")
        else:
            print("Type Ok")

NamedTuple توابعی مانند _asdict را نشان می دهد که شی را به یک فرهنگ لغت تبدیل می کند که می تواند برای جستجوی داده ها استفاده شود. همچنین ویژگی هایی مانند __annotations__ را که یک فرهنگ لغت است انواع را برای هر فیلد ذخیره می کند، در معرض دید قرار می دهد و برای اطلاعات بیشتر در مورد __annotations__، بررسی نوع پایتون (راهنما) را بررسی کنید.

همانطور که در بررسی نوع پایتون (راهنما) مشخص شده است، می توانید از بسته های موجود مانند Enforce، Pydantic و Pytypes برای اعتبارسنجی پیشرفته نیز استفاده کنید.

اعتبارسنجی سفارشی

برخلاف آنچه قبلا بررسی کردید، اعتبارسنجی دقیق ممکن است به رویکردهای سفارشی نیاز داشته باشد. به عنوان مثال، اگر بخواهید sha1sum_stdin.py را با نام فایل نادرست به عنوان آرگومان اجرا کنید، موارد زیر را دریافت می کنید:

python sha1sum_stdin.py bad_file.txt
Traceback (most recent call last):
  File "sha1sum_stdin.py", line 32, in <module>
    main(sys.argv[1:])
  File "sha1sum_stdin.py", line 29, in main
    output_sha1sum(process_file(arg), arg)
  File "sha1sum_stdin.py", line 9, in process_file
    return pathlib.Path(filename).read_bytes()
  File "/usr/lib/python3.8/pathlib.py", line 1222, in read_bytes
    with self.open(mode='rb') as f:
  File "/usr/lib/python3.8/pathlib.py", line 1215, in open
    return io.open(self, mode, buffering, encoding, errors, newline,
  File "/usr/lib/python3.8/pathlib.py", line 1071, in _opener
    return self._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2] No such file or directory: 'bad_file.txt'

bad_file.txt وجود ندارد، اما برنامه سعی می کند آن را بخواند.

main() را در sha1sum_stdin.py برای مدیریت فایل های غیر موجود ارسال شده در خط فرمان دوباره بازدید کنید:

def main(args):
    if not args:
        output_sha1sum(process_stdin())
    for arg in args:
        if arg == "-":
            output_sha1sum(process_stdin(), "-")
            continue
        try:
            output_sha1sum(process_file(arg), arg)
        except FileNotFoundError as err:
            print(f"{sys.argv[0]}: {arg}: {err.strerror}", file=sys.stderr)

برای دیدن مثال کامل با این اعتبارسنجی اضافی، بلوک کد زیر را گسترش دهید:

sha1sum_val.py

from typing import List
import hashlib
import pathlib
import sys

def process_file(filename: str) -> bytes:
    return pathlib.Path(filename).read_bytes()

def process_stdin() -> bytes:
    return bytes("".join(sys.stdin), "utf-8")

def sha1sum(data: bytes) -> str:
    m = hashlib.sha1()
    m.update(data)
    return m.hexdigest()

def output_sha1sum(data: bytes, filename: str = "-") -> None:
    print(f"{sha1sum(data)}  {filename}")

def main(args: List[str]) -> None:
    if not args:
        output_sha1sum(process_stdin())
    for arg in args:
        if arg == "-":
            output_sha1sum(process_stdin(), "-")
            continue
        try:
            output_sha1sum(process_file(arg), arg)
        except (FileNotFoundError, IsADirectoryError) as err:
            print(f"{sys.argv[0]}: {arg}: {err.strerror}", file=sys.stderr)

if __name__ == "__main__":
    main(sys.argv[1:])

هنگامی که این اسکریپت اصلاح شده را اجرا می کنید، این را دریافت می کنید:

python sha1sum_val.py bad_file.txt
sha1sum_val.py: bad_file.txt: No such file or directory

توجه داشته باشید که خطای نمایش داده شده در ترمینال به stderr نوشته شده است، بنابراین با داده های مورد انتظار دستوری که خروجی sha1sum_val.py را می خواند تداخل ندارد:

python sha1sum_val.py bad_file.txt main.py | cut -d " " -f 1
sha1sum_val.py: bad_file.txt: No such file or directory
d84372fc77a90336b6bb7c5e959bcb1b24c608b4

این فرمان خروجی sha1sum_val.py را برای برش لوله می کند تا فقط فیلد اول را شامل شود. می بینید که برش پیام خطا را نادیده می گیرد زیرا فقط داده های ارسال شده به stdout را دریافت می کند.

کتابخانه استاندارد پایتون

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

    argparse
    getopt
    optparse

ماژول توصیه شده برای استفاده از کتابخانه استاندارد argparse است. کتابخانه استاندارد نیز optparse را در معرض دید قرار می دهد اما به طور رسمی منسوخ شده است و فقط برای اطلاعات شما در اینجا ذکر شده است. در پایتون 3.2 با argparse جایگزین شد و در این آموزش نخواهید دید که در مورد آن بحث شده است.

ارگپارس

شما می خواهید sha1sum_val.py، جدیدترین کلون sha1sum را دوباره مرور کنید تا مزایای argparse را معرفی کنید. برای این منظور، main() را تغییر می دهید و init_argparse را برای نمونه سازی argparse اضافه می کنید. آرگومان پارسر:

import argparse

def init_argparse() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        usage="%(prog)s [OPTION] [FILE]...",
        description="Print or check SHA1 (160-bit) checksums."
    )
    parser.add_argument(
        "-v", "--version", action="version",
        version = f"{parser.prog} version 1.0.0"
    )
    parser.add_argument('files', nargs='*')
    return parser

def main() -> None:
    parser = init_argparse()
    args = parser.parse_args()
    if not args.files:
        output_sha1sum(process_stdin())
    for file in args.files:
        if file == "-":
            output_sha1sum(process_stdin(), "-")
            continue
        try:
            output_sha1sum(process_file(file), file)
        except (FileNotFoundError, IsADirectoryError) as err:
            print(f"{sys.argv[0]}: {file}: {err.strerror}", file=sys.stderr)

با هزینه چند خط بیشتر در مقایسه با پیاده سازی قبلی، یک رویکرد تمیز برای اضافه کردن گزینه های --help و --version دریافت می کنید که قبلا وجود نداشت. آرگومان های مورد انتظار (فایل هایی که باید پردازش شوند) همگی در فایل های فیلدی شی argparse موجود هستند. فضای نام استفاده کنید. این شی در خط 17 با فراخوانی parse_args()پر می شود.

برای مشاهده اسکریپت کامل با تغییراتی که در بالا توضیح داده شد، بلوک کد زیر را گسترش دهید:

sha1sum_argparse.py

import argparse
import hashlib
import pathlib
import sys

def process_file(filename: str) -> bytes:
    return pathlib.Path(filename).read_bytes()

def process_stdin() -> bytes:
    return bytes("".join(sys.stdin), "utf-8")

def sha1sum(data: bytes) -> str:
    sha1_hash = hashlib.sha1()
    sha1_hash.update(data)
    return sha1_hash.hexdigest()

def output_sha1sum(data: bytes, filename: str = "-") -> None:
    print(f"{sha1sum(data)}  {filename}")

def init_argparse() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        usage="%(prog)s [OPTION] [FILE]...",
        description="Print or check SHA1 (160-bit) checksums.",
    )
    parser.add_argument(
        "-v", "--version", action="version",
        version=f"{parser.prog} version 1.0.0"
    )
    parser.add_argument("files", nargs="*")
    return parser

def main() -> None:
    parser = init_argparse()
    args = parser.parse_args()
    if not args.files:
        output_sha1sum(process_stdin())
    for file in args.files:
        if file == "-":
            output_sha1sum(process_stdin(), "-")
            continue
        try:
            output_sha1sum(process_file(file), file)
        except (FileNotFoundError, IsADirectoryError) as err:
            print(f"{parser.prog}: {file}: {err.strerror}", file=sys.stderr)

if __name__ == "__main__":
    main()

برای نشان دادن سود فوری که با معرفی argparse در این برنامه به دست می آورید، موارد زیر را اجرا کنید:

python sha1sum_argparse.py --help
usage: sha1sum_argparse.py [OPTION] [FILE]...

Print or check SHA1 (160-bit) checksums.

positional arguments:
  files

optional arguments:
  -h, --help     show this help message and exit
  -v, --version  show program's version number and exit

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

Getopt

getopt ریشه خود را در تابع getopt C می یابد. تجزیه خط فرمان و مدیریت گزینه ها، آرگومان های گزینه و آرگومان ها را تسهیل می کند. برای استفاده از getopt دوباره به تجزیه از seq_parse.py مراجعه کنید:

def parse():
    options, arguments = getopt.getopt(
        sys.argv[1:],                      # Arguments
        'vhs:',                            # Short option definitions
        ["version", "help", "separator="]) # Long option definitions
    separator = "\n"
    for o, a in options:
        if o in ("-v", "--version"):
            print(VERSION)
            sys.exit()
        if o in ("-h", "--help"):
            print(USAGE)
            sys.exit()
        if o in ("-s", "--separator"):
            separator = a
    if not arguments or len(arguments) > 3:
        raise SystemExit(USAGE)
    try:
        operands = [int(arg) for arg in arguments]
    except ValueError:
        raise SystemExit(USAGE)
    return separator, operands

getopt.getopt() آرگومان های زیر را می گیرد:

  1. آرگومان های معمول منهای نام اسکریپت، sys.argv[1:]
  2. رشته ای که گزینه های کوتاه را تعریف می کند
  3. لیستی از رشته ها برای گزینه های طولانی

توجه داشته باشید که یک گزینه کوتاه و به دنبال آن دو نقطه (:) انتظار آرگومان گزینه را دارد و یک گزینه طولانی که با علامت مساوی (=) دنبال می شود، انتظار آرگومان گزینه را دارد.

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

seq_getopt.py

from typing import List, Tuple
import getopt
import sys

USAGE = f"Usage: python {sys.argv[0]} [--help] | [-s <sep>] [first [incr]] last"
VERSION = f"{sys.argv[0]} version 1.0.0"

def seq(operands: List[int], sep: str = "\n") -> str:
    first, increment, last = 1, 1, 1
    if len(operands) == 1:
        last = operands[0]
    elif len(operands) == 2:
        first, last = operands
        if first > last:
            increment = -1
    elif len(operands) == 3:
        first, increment, last = operands
    last = last - 1 if first > last else last + 1
    return sep.join(str(i) for i in range(first, last, increment))

def parse(args: List[str]) -> Tuple[str, List[int]]:
    options, arguments = getopt.getopt(
        args,                              # Arguments
        'vhs:',                            # Short option definitions
        ["version", "help", "separator="]) # Long option definitions
    separator = "\n"
    for o, a in options:
        if o in ("-v", "--version"):
            print(VERSION)
            sys.exit()
        if o in ("-h", "--help"):
            print(USAGE)
            sys.exit()
        if o in ("-s", "--separator"):
            separator = a
    if not arguments or len(arguments) > 3:
        raise SystemExit(USAGE)
    try:
        operands = [int(arg) for arg in arguments]
    except:
        raise SystemExit(USAGE)
    return separator, operands

def main() -> None:
    args = sys.argv[1:]
    if not args:
        raise SystemExit(USAGE)
    sep, operands = parse(args)
    print(seq(operands, sep))

if __name__ == "__main__":
    main()

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

چند پکیج خارجی پایتون

با تکیه بر قراردادهای موجود که در این آموزش مشاهده کردید، چند کتابخانه در Python Package Index (PyPI) موجود است که اقدامات بسیار بیشتری را برای تسهیل پیاده سازی و نگهداری رابط های خط فرمان انجام می دهند.

بخش های زیر نگاهی به جعبه ابزار Click و Python Prompt ارائه می دهند. شما فقط در معرض قابلیت های بسیار محدود این بسته ها قرار خواهید گرفت، زیرا هر دو به یک آموزش کامل نیاز دارند - اگر نه یک سری کامل - تا عدالت را رعایت کنند!

کلیک کنید

در زمان نگارش این مقاله، Click شاید پیشرفته ترین کتابخانه برای ساخت یک رابط خط فرمان پیچیده برای یک برنامه پایتون باشد. توسط چندین محصول پایتون، به ویژه Flask و Black استفاده می شود. قبل از اینکه مثال زیر را امتحان کنید، باید Click را در یک محیط مجازی پایتون یا محیط محلی خود نصب کنید. اگر با مفهوم محیط های مجازی آشنا نیستید، محیط های مجازی پایتون: یک پرایمر را بررسی کنید.

برای نصب Click، به صورت زیر عمل کنید:

python -m pip install click

بنابراین، چگونه کلیک می تواند به شما در مدیریت آرگومان های خط فرمان پایتون کمک کند؟ در اینجا یک تغییر از برنامه seq با استفاده از Click آورده شده است:

seq_click.py

import click

@click.command(context_settings=dict(ignore_unknown_options=True))
@click.option("--separator", "-s",
              default="\n",
              help="Text used to separate numbers (default: \\n)")
@click.version_option(version="1.0.0")
@click.argument("operands", type=click.INT, nargs=-1)
def seq(operands, separator) -> str:
    first, increment, last = 1, 1, 1
    if len(operands) == 1:
        last = operands[0]
    elif len(operands) == 2:
        first, last = operands
        if first > last:
            increment = -1
    elif len(operands) == 3:
        first, increment, last = operands
    else:
        raise click.BadParameter("Invalid number of arguments")
    last = last - 1 if first > last else last + 1
    print(separator.join(str(i) for i in range(first, last, increment)))

if __name__ == "__main__":
    seq()

تنظیم ignore_unknown_options روی True تضمین می کند که Click آرگومان های منفی را به عنوان گزینه تجزیه نمی کند. اعداد صحیح منفی آرگومان های seq معتبری هستند.

همانطور که ممکن است مشاهده کرده باشید، چیزهای زیادی به صورت رایگان دریافت می کنید! چند دکوراتور خوب حکاکی شده برای دفن کد دیگ بخار کافی هستند و به شما امکان می دهند روی کد اصلی تمرکز کنید، که محتوای seq() در این مثال است.

تنها واردات باقی مانده کلیک است. رویکرد اعلامی تزئین دستور اصلی، seq()، کدهای تکراری را که در غیر این صورت ضروری هستند حذف می کند. این می تواند یکی از موارد زیر باشد:

  • تعریف یک رویه راهنما یا استفاده
  • مدیریت نسخه برنامه
  • ضبط و تنظیم مقادیر پیش فرض برای گزینه ها
  • اعتبارسنجی آرگومان ها، از جمله نوع

پیاده سازی seq جدید به سختی سطح را خراش می دهد. Click نکات ظرافت زیادی را ارائه می دهد که به شما کمک می کند یک رابط خط فرمان بسیار حرفه ای بسازید:

  • رنگ آمیزی خروجی
  • درخواست برای استدلال های حذف شده
  • دستورات و فرمان های فرعی
  • اعتبارسنجی نوع آرگومان
  • پاسخ به تماس در گزینه ها و آرگومان ها
  • اعتبارسنجی مسیر فایل
  • نوار پیشرفت

بسیاری از ویژگی های دیگر نیز وجود دارد. نوشتن ابزارهای خط فرمان پایتون با کلیک را بررسی کنید تا نمونه های ملموس بیشتری را بر اساس کلیک ببینید.

جعبه ابزار Python Prompt

بسته های محبوب پایتون دیگری نیز وجود دارند که مشکل رابط خط فرمان را مدیریت می کنند، مانند docopt برای پایتون. بنابراین، ممکن است انتخاب Prompt Toolkit را کمی غیرمنطقی بدانید.

جعبه ابزار Python Prompt ویژگی هایی را ارائه می دهد که ممکن است برنامه خط فرمان شما را از فلسفه یونیکس دور کند. با این حال، به پر کردن شکاف بین یک رابط خط فرمان محرمانه و یک رابط کاربری گرافیکی تمام عیار کمک می کند. به عبارت دیگر، ممکن است به کاربرپسندتر کردن ابزارها و برنامه های شما کمک کند.

شما می توانید از این ابزار علاوه بر پردازش آرگومان های خط فرمان پایتون مانند مثال های قبلی استفاده کنید، اما این به شما مسیری برای یک رویکرد شبیه به رابط کاربری می دهد بدون اینکه مجبور باشید به یک جعبه ابزار کامل رابط کاربری پایتون وابسته باشید. برای استفاده از prompt_toolkit ، باید آن را با pip نصب کنید:

python -m pip install prompt_toolkit

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

همانطور که قبلا منطق اصلی این مثال را مشاهده کردید، قطعه کد زیر فقط کدی را ارائه می دهد که به طور قابل توجهی از مثال های قبلی منحرف می شود:

def error_dlg():
    message_dialog(
        title="Error",
        text="Ensure that you enter a number",
    ).run()

def seq_dlg():
    labels = ["FIRST", "INCREMENT", "LAST"]
    operands = []
    while True:
        n = input_dialog(
            title="Sequence",
            text=f"Enter argument {labels[len(operands)]}:",
        ).run()
        if n is None:
            break
        if n.isdigit():
            operands.append(int(n))
        else:
            error_dlg()
        if len(operands) == 3:
            break

    if operands:
        seq(operands)
    else:
        print("Bye")        

actions = {"SEQUENCE": seq_dlg, "HELP": help, "VERSION": version}

def main():
    result = button_dialog(
        title="Sequence",
        text="Select an action:",
        buttons=[
            ("Sequence", "SEQUENCE"),
            ("Help", "HELP"),
            ("Version", "VERSION"),
        ],
    ).run()
    actions.get(result, lambda: print("Unexpected action"))()

کد بالا شامل روش هایی برای تعامل و احتمالا راهنمایی کاربران برای وارد کردن ورودی مورد انتظار و اعتبارسنجی تعاملی ورودی با استفاده از سه کادر محاوره ای است:

    button_dialog
    message_dialog
    input_dialog

جعبه ابزار Python Prompt بسیاری از ویژگی های دیگر را که برای بهبود تعامل با کاربران در نظر گرفته شده اند، نشان می دهد. فراخوانی به کنترل کننده در main() با فراخوانی یک تابع ذخیره شده در یک فرهنگ لغت فعال می شود. اگر قبلا با این اصطلاح پایتون مواجه نشده اید، شبیه سازی دستورات سوئیچ/کیس در پایتون را بررسی کنید.

با گسترش بلوک کد زیر می توانید نمونه کامل برنامه با استفاده از prompt_toolkit را مشاهده کنید:

seq_prompt.py

import sys
from typing import List
from prompt_toolkit.shortcuts import button_dialog, input_dialog, message_dialog

def version():
    print("Version 1.0.0")

def help():
    print("Print numbers from FIRST to LAST, in steps of INCREMENT.")

def seq(operands: List[int], sep: str = "\n"):
    first, increment, last = 1, 1, 1
    if len(operands) == 1:
        last = operands[0]
    elif len(operands) == 2:
        first, last = operands
        if first > last:
            increment = -1
    elif len(operands) == 3:
        first, increment, last = operands
    last = last - 1 if first > last else last + 1
    print(sep.join(str(i) for i in range(first, last, increment)))

def error_dlg():
    message_dialog(
        title="Error",
        text="Ensure that you enter a number",
    ).run()

def seq_dlg():
    labels = ["FIRST", "INCREMENT", "LAST"]
    operands = []
    while True:
        n = input_dialog(
            title="Sequence",
            text=f"Enter argument {labels[len(operands)]}:",
        ).run()
        if n is None:
            break
        if n.isdigit():
            operands.append(int(n))
        else:
            error_dlg()
        if len(operands) == 3:
            break

    if operands:
        seq(operands)
    else:
        print("Bye")        

actions = {"SEQUENCE": seq_dlg, "HELP": help, "VERSION": version}

def main():
    result = button_dialog(
        title="Sequence",
        text="Select an action:",
        buttons=[
            ("Sequence", "SEQUENCE"),
            ("Help", "HELP"),
            ("Version", "VERSION"),
        ],
    ).run()
    actions.get(result, lambda: print("Unexpected action"))()

if __name__ == "__main__":
    main()

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

همانطور که خط فرمان تکامل می یابد و می توانید تلاش هایی برای تعامل خلاقانه تر با کاربران مشاهده کنید، بسته های دیگر مانند PyInquirer نیز به شما امکان می دهند از یک رویکرد بسیار تعاملی استفاده کنید.

برای کاوش بیشتر در دنیای رابط کاربری مبتنی بر متن (TUI)، رابط های کاربری کنسول ساختمان و بخش شخص ثالث را در راهنمای عملکرد چاپ پایتون بررسی کنید.

اگر علاقه مند به تحقیق در مورد راه حل هایی هستید که منحصرا به رابط کاربری گرافیکی متکی هستند، می توانید منابع زیر را بررسی کنید:

  • چگونه یک اپلیکیشن رابط کاربری گرافیکی پایتون با wxPython بسازیم؟
  • پایتون و PyQt: ساخت یک ماشین حساب دسکتاپ رابط کاربری گرافیکی
  • ساخت یک اپلیکیشن موبایل با فریمورک Kivy Python

نتیجه

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

  • قراردادها و شبه استانداردهای آرگومان های خط فرمان پایتون
  • ریشه sys.argv در پایتون
  • استفاده از sys.argv برای ایجاد انعطاف پذیری در اجرای برنامه های پایتون
  • کتابخانه های استاندارد پایتون مانند argparse یا getopt که پردازش خط فرمان انتزاعی را انجام می دهند
  • بسته های قدرتمند پایتون مانند کلیک و python_toolkit برای بهبود بیشتر قابلیت استفاده از برنامه های شما

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

دفعه بعد که از برنامه خود استفاده می کنید، از اسنادی که با گزینه --help ارائه کرده اید یا این واقعیت که می توانید به جای تغییر کد منبع برای ارائه داده های مختلف، گزینه ها و آرگومان ها را ارسال کنید، قدردانی خواهید کرد.

منابع اضافی

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

  • مقایسه کتابخانه های تجزیه خط فرمان پایتون - Argparse ،Docopt و Click
  • پایتون، روبی و گولانگ: مقایسه اپلیکیشن خط فرمان

همچنین ممکن است بخواهید سایر کتابخانه های پایتون را امتحان کنید که مشکلات مشابهی را هدف قرار می دهند و در عین حال راه حل های مختلفی را به شما ارائه می دهند:

  • تایپر
  • پلاک
  • صخره
  • سیمان
  • پایتون فایر