Giải pháp do @Vikas cung cấp không thành công đối với các đối số tùy chọn dành riêng cho lệnh con, nhưng phương pháp này hợp lệ. Đây là một phiên bản cải tiến:
import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
argv = ['--foo', 'command_a', '12', 'command_b', '--baz', 'Z']
while argv:
print(argv)
options, argv = parser.parse_known_args(argv)
print(options)
if not options.subparser_name:
break
Điều này sử dụng parse_known_args
thay vì parse_args
. parse_args
hủy bỏ ngay khi gặp phải đối số không xác định đối với subparser hiện tại,parse_known_args
trả về chúng dưới dạng giá trị thứ hai trong bộ giá trị trả về. Trong cách tiếp cận này, các đối số còn lại được cấp lại cho trình phân tích cú pháp. Vì vậy, đối với mỗi lệnh, một Namespace mới được tạo.
Lưu ý rằng trong ví dụ cơ bản này, tất cả các tùy chọn toàn cục chỉ được thêm vào các tùy chọn đầu tiên Không gian tên, không cho các Không gian tên tiếp theo.
Cách tiếp cận này hoạt động tốt cho hầu hết các tình huống, nhưng có ba hạn chế quan trọng:
- Không thể sử dụng cùng một đối số tùy chọn cho các lệnh con khác nhau, chẳng hạn như
myprog.py command_a --foo=bar command_b --foo=bar
.
- Không thể sử dụng bất kỳ đối số vị trí có độ dài thay đổi nào với các lệnh con (
nargs='?'
hoặc nargs='+'
hoặcnargs='*'
).
- Bất kỳ đối số đã biết nào đều được phân tích cú pháp mà không bị 'ngắt' ở lệnh mới. Ví dụ:
PROG --foo command_b command_a --baz Z 12
với mã trên, --baz Z
sẽ được tiêu thụ bởi command_b
, không phải bởi command_a
.
Những hạn chế này là một hạn chế trực tiếp của argparse. Đây là một ví dụ đơn giản cho thấy những hạn chế của argparse -even khi sử dụng một lệnh con-:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('spam', nargs='?')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
options = parser.parse_args('command_a 42'.split())
print(options)
Điều này sẽ nâng cao error: argument subparser_name: invalid choice: '42' (choose from 'command_a', 'command_b')
.
Nguyên nhân là do phương thức bên trong argparse.ArgParser._parse_known_args()
nó quá tham lam và cho rằng đó command_a
là giá trị của spam
đối số tùy chọn . Đặc biệt, khi 'tách' các đối số tùy chọn và vị trí, _parse_known_args()
không nhìn vào tên của các cung (như command_a
hoặc command_b
), mà chỉ nhìn vào vị trí chúng xuất hiện trong danh sách đối số. Nó cũng giả định rằng bất kỳ lệnh con nào sẽ sử dụng tất cả các đối số còn lại. Hạn chế này argparse
cũng ngăn cản việc triển khai đúng các chương trình con đa lệnh. Điều này không may có nghĩa là việc triển khai đúng yêu cầu viết lại toàn bộ argparse.ArgParser._parse_known_args()
phương thức, tức là hơn 200 dòng mã.
Với những hạn chế này, nó có thể là một tùy chọn để chỉ cần hoàn nguyên về một đối số nhiều lựa chọn thay vì các lệnh con:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--bar', type=int, help='bar help')
parser.add_argument('commands', nargs='*', metavar='COMMAND',
choices=['command_a', 'command_b'])
options = parser.parse_args('--bar 2 command_a command_b'.split())
print(options)
Thậm chí có thể liệt kê các lệnh khác nhau trong thông tin sử dụng, hãy xem câu trả lời của tôi https://stackoverflow.com/a/49999185/428542