Nattawut Phetmak
Jack of all Trades
การสร้าง CLI สำหรับ script ที่รับตัวแปรตอนเรียกโปรแกรมนั้น แม้จะใช้ sys.argv
เพื่อดึงส่วน argument ได้ แต่เมื่อต้องทำงานที่ซับซ้อนแล้ว module ที่จะช่วยให้งานนี้เป็นระเบียบเรียบร้อยคือ argparse
ตัวอย่างเช่น โปรแกรม sq.py
ที่จะทำการยกกำลังสองเลขที่รับเข้ามา
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('num')
args = parser.parse_args()
print(int(args.num)**2)
เมื่อเรียกโปรแกรมนี้ผ่านทาง CLI จะได้ผลลัพท์ดังนี้
$ python sq.py
usage: sq.py [-h] num
sq.py: error: too few arguments
$
$ python sq.py 19
361
สังเกตกว่า num มีประเภทตัวแปรเป็น string ก่อนที่จะนำไปใช้คำนวณต้องแปลงค่าให้เป็น int ก่อน ซึ่งเราสามารถบอกประเภทให้ตัว parser แต่แรกเลยก็ได้ โดยแก้ไขบรรทัดที่ 4 เป็น
parser.add_argument('num', type=int)
จากตัวอย่างที่แล้ว ตัวแปร num คือตัวแปรแบบ positional argument ซึ่งหมายความว่า ต้องป้อนตัวแปรเหล่านี้ให้ครบ
ลองดูโปรแกรม acal.py
ที่รับตัวเลข 3 ตัว แล้วหาผลรวม
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('num', type=int, nargs=3)
args = parser.parse_args()
print(sum(args.num))
นอกจากกำหนดจำนวน argument ด้วยตัวเลขแล้ว ยังสามารถใช้ wildcard ?, +, * เพื่อบอกจำนวนก็ย่อมได้ เช่นปรับปรุงโปรแกรมข้างต้นให้รับ argument ได้ไม่จำกัดโดยเปลี่ยนบรรทัดที่ 4 เป็น
parser.add_argument('num', type=int, nargs='+')
สำหรับตัวแปรอีกประเภทคือ optional argument เช่น -h
โดยมีวิธีเขียนที่แตกต่างออกไปเล็กน้อย
คราวนี้มาปรับปรุง acal.py
ให้รับ optional argument เพื่อให้สามารถเลือก max หรือ min แทนการ sum ได้
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('num', type=int, nargs='+')
parser.add_argument('-M', '--max', dest='main', action='store_const',
const=max, default=sum)
parser.add_argument('-m', '--min', dest='main', action='store_const',
const=min, default=sum)
args = parser.parse_args()
print(args.main(args.num))
และยังมี optional argument แบบที่สามารถรับ argument ต่อท้ายอีกได้ด้วย ลองดูตัวอย่างการเปิดไฟล์สำหรับเขียนโดยเพิ่มคำสั่งนี้เข้าไปหลังบรรทัดที่ 8
parser.add_argument('-f', '--file', metavar='FILE', dest='file',
type=argparse.FileType('w'))
เนื่องจากการเปิดไฟล์ด้วย open
เพื่อเขียนต้องบอก option 'w'
ด้วย แต่เพราะ type รับตัวแปรเดียว ตรงนี้ให้ใช้ argparse.FileType('w')
แทน (หรือจะใชประยุกต์ใช้ lambda f: open(f, 'w')
ก็ย่อมได้)
จุดสังเกตอีกอย่างที่เพิ่มขึ้นมาคือ metavar
ซึ่งเป็นการบ่งว่า optional argument นี้รับตัวแปรเพิ่มได้ แต่ถ้าเอาไปใส่ให้กับ positional argument จะกลายเป็นแค่การบอกชื่อย่อเท่านั้น
หลังจากเปิดไฟล์กันได้แล้ว ถึงตอนนี้ก็ต้องจัดการกับการแสดงผลที่บรรทัด 11 ใหม่หมด
output = args.main(args.num)
if args.file:
args.file.write(str(output))
args.file.close()
else:
print(output)
คราวนี้ก็ลองเขียน help เต็มรูปแบบให้กับ acal.py
กันดูครับ
import argparse
parser = argparse.ArgumentParser(description='Advance Calculator.',
epilog='WTFPL (C) 2012 neizod')
parser.add_argument('numbers', metavar='N', type=int, nargs='+',
help='list of numbers for summation')
parser.add_argument('-M', '--max', dest='main', action='store_const',
const=max, default=sum, help='find max insteed')
parser.add_argument('-m', '--min', dest='main', action='store_const',
const=min, default=sum, help='find min instead')
parser.add_argument('-f', '--file', metavar='FILE', dest='file',
type=argparse.FileType('w'),
help='store output to a file')
args = parser.parse_args()
output = args.main(args.numbers)
if args.file:
args.file.write(str(output))
args.file.close()
else:
print(output)