argparse - โยน parameter เข้า Python แบบ next level
ผมคิดว่า ผู้อ่านคงคุ้นเคยกับการใช้ bash (ใน UNIX) หรือไม่ก็ powershell (ใน Windows) ซึ่งทั่วไปคือการเรียกใช้โปรแกรมพร้อมกับ parameter สักตัวสองตัว
เราทำแบบนั้นกับ Python script ได้เหมือนกันฮะ
ทีนี้ สถานการณ์ที่เราจะทำแบบนั้นมันเป็นไปได้หลายกรณี เช่น
เราเขียนโปรแกรมง่ายๆ สักตัวแล้วต้องการเอาไปต่อกับอีกระบบนึง โดยการเรียกและส่ง parameter ระหว่างกัน
หรือเราต้องการผลลัพท์จาก Python script ที่เรามีอยู่แล้วแหละ แต่ parameter จะมาจาก environment variables แล้วเราต้องการให้มันใช้งานใน CI/CD process
เริ่มซับซ้อนเนอะฮะ งั้นเราค่อยๆ คุยกันในบล็อคนี้ ทีละขั้นนะฮะ
solution พื้นฐานสุด
ก่อนอื่นเลย parameter ตอนที่เราเรียกแบบ bash command line เนี่ย มันจะมองเป็น list of string ฮะ ซึ่งจะเรียกได้ผ่านตัวแปร sys.argv
แบบนี้
ลองโยน parameter ไปหน่อยซิ
python3 src/argv/argv01_simple.py 1 2
"1 2" จะถูกโยนเข้าไปที่ sys.argv
แล้วให้ method get_params()
แงะมันออกมาใส่ตัวแปร a
, b
แล้วเราจะได้ผลลัพท์หน้าตาแบบนี้
ถ้าไม่มีแค่สองตัวแบบข้างบน แต่มาหลายตัว เราก็ทำแบบนี้ได้ฮะ
ใช้ sum
แทน ก็จะได้ผลลัพท์แบบนี้แทน
ประเด็นจริงๆ ไม่ได้อยู่ที่รับมากี่ตัว แต่จะเป็นเรื่องของ flag มากกว่า แบบถ้าเราต้องการให้สามารถรับ "flags" ได้ เช่น
python3 my-script.py 1 2 3 --flag1 ABC --flag2 XYZ
เราคงต้องเขียน funtion มาเช็คว่าค่าไหนเป็น flag ไหนบ้าง ซับซ้อนไปอีก
ลากมาเข้าเรื่องได้แล้วล่ะฮะ เพราะบล็อคนี้ เราจะมารู้จักกับ...
argparse
module
argparse
เป็น module จัดการ parameter กับ flag ที่เล่าๆ ไว้ข้างต้น รวมถึงช่วยทำ help และ error handling แบบเบื้องต้นให้ด้วยนะ ตัวเอกสารทางการของ argparse
อยู่ลิงก?ข้างล่างนี้แล้วฮะ
มาลองของจริงกันเลยดีกว่าเนอะ
บวกเลขสองตัวง่ายๆ
หัวใจหลักมันอยู่ตรงนี้ฮะ
parser = argparse.ArgumentParser()
parser.add_argument(...)
parsed_params = parser.parse_args(sys.argv[1:])
เราส่งค่า sys.argv[1:]
ไป เหมือนกับข้างบนนั่นแหละ จากนั้นเราสร้าง parser ขึ้นมาแล้ว .add_argument()
เพื่อกำหนดเงื่อนไขของแต่ละ parameter
จากตัวอย่าง มันมีสามค่าหลักๆ ได้แก่
- string ของแต่ละ parameter
type
กรณีนี้เป็นstr
แปลว่าเราจะรับค่า parameter นี้มาเป็น string นั่นเองhelp
เป็น help message ไว้แสดงผลตอนใช้-h
flag ตัวอย่างเช่น
พอ argparse
แยก parameters เสร็จ เราก็สามารถ access ด้วย syntax obj.name
ได้ฮะ
ผลลัพท์โปรแกรมจะประมาณนี้นะ
บวกเลขจาก list
ที่บรรทัด 9 จะเห็นว่ามี nargs="+"
ตรงนี้แหละฮะ ที่เราสามารถกำหนดให้ parameter ชื่อนี้เป็น list ได้ เครื่องหมายบวกแปลว่าจะมีค่าตั้งแต่หนึ่งค่าขึ้นไปนะ โปรแกรมนี้จะให้ผลลัพท์ราวนี้ฮะ
บวกเลขจาก list แล้วมี choice ด้วย
เราสามารถกำหนดให้มี flag หลายค่าเพื่อเก็บเป็น parameter ตัวเดียวกันได้ เช่น ดูที่บรรทัด 17-18 กำหนดให้เป็น flag -o
และ --ops
โดยชื่อแรกที่ขึ้นต้นด้วย --
จะเป็นชื่อ flag ให้เราเรียกใช้ในโปรแกรมฮะ อย่างตัวอย่างนี้ก็จะเป็นชื่อ ops
นั่นเองฮะ
choices
ให้เรากำหนดตัวเลือกที่จะรับค่าได้ ถ้าตอน run จริงป้อนค่าอื่น ก็จะฟ้อง error ฮะ
จากตัวอย่างเนี่ย ถ้าเราลอง add
ก็จะได้ผลรวมบวกทุกเลข
แล้วถ้าเป็น mult
ก็จะได้ผลรวมคูณของทุกเลขเหมือนกันฮะ
ทำ web scraper ใส่ file พร้อม overwriting handler
ตัวอย่างสุดท้าย คือ download website เก็บลง file โดยมีเงื่อนไขว่า ถ้ามี file อยู่แล้วและไม่กำหนดให้เขียนทับ (overwrite) ให้ฟ้อง error นะ
เราจะกำหนด flag --overwrite
หรือ -w
พร้อมกำกับไว้ว่า action="store_true"
ตรงนี้หมายถึง ถ้าป้อน flag นี้เข้ามา จะทำให้ parameter overwrite
มีค่าเป็น true ทันที แต่ถ้าไม่ป้อน ก็จะมีค่าเป็น false
ทีนี้ ลองให้มี file ชื่อที่ว่าอยู่ก่อนแล้ว และเราไม่ป้อน -w
จะเจอกับ error ฮะ
พอป้อน -w
โปรแกรมก็จะเขียนทับ file ทันที
อันนี้เป็น repo ของผมเองฮะ มีทั้ง script ข้างบน พร้อมตัวอย่าง command ให้ลองเล่นกันได้ฮะ
ด้วยความยืดหยุ่นของ argparse
ทำให้เราสามารถออกแบบโปรแกรมของเราได้หลาดหลายมากๆ ฮะ