2011-12-15 27 views
17

Tôi có một kịch bản mà tôi yêu cầu người dùng cho một danh sách các hành động được xác định trước để thực hiện. Tôi cũng muốn khả năng giả định một danh sách các hành động cụ thể khi người dùng không xác định bất cứ điều gì. tuy nhiên, có vẻ như cố gắng làm cả hai việc này với nhau là không thể.python argparse - tham số chắp thêm tùy chọn với các lựa chọn

khi người dùng không đưa ra lý lẽ, họ nhận được một lỗi mà các lựa chọn mặc định là không hợp lệ

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args([]) 
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]] 
: error: argument action: invalid choice: [['dump', 'clear']] (choose from 'clear', 'copy', 'dump', 'lock') 

và khi họ làm định nghĩa một tập các hành động, các namespace kết quả có những hành động của người dùng nối vào mặc định, thay vì thay thế mặc định

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args(['lock']) 
args 
>>> Namespace(action=[['dump', 'clear'], ['dump']]) 
+0

Tôi không chắc chắn nếu điều này đã được giải quyết bởi một lỗi tương tự đã được báo cáo: http://bugs.python.org/issue9625. Một cách có thể để xử lý việc này là sử dụng một hành động tùy chỉnh, thay vì từ khóa 'choice'. Xem câu trả lời được chấp nhận về [câu hỏi này] (http://stackoverflow.com/questions/4194948/python-argparse-is-there-a-way-to-specify-a-range-in-nargs) – Chris

Trả lời

15

Những gì bạn cần thể được thực hiện bằng cách sử dụng tùy chỉnh argparse.Action như trong ví dụ sau:

import argparse 

parser = argparse.ArgumentParser() 

class DefaultListAction(argparse.Action): 
    CHOICES = ['clear','copy','dump','lock'] 
    def __call__(self, parser, namespace, values, option_string=None): 
     if values: 
      for value in values: 
       if value not in self.CHOICES: 
        message = ("invalid choice: {0!r} (choose from {1})" 
           .format(value, 
             ', '.join([repr(action) 
                for action in self.CHOICES]))) 

        raise argparse.ArgumentError(self, message) 
      setattr(namespace, self.dest, values) 

parser.add_argument('actions', nargs='*', action=DefaultListAction, 
        default = ['dump', 'clear'], 
        metavar='ACTION') 

print parser.parse_args([]) 
print parser.parse_args(['lock']) 

Đầu ra của kịch bản là:

$ python test.py 
Namespace(actions=['dump', 'clear']) 
Namespace(actions=['lock']) 
1

Hành động này đã được thêm vào vì thông số "action = 'append'" mà bạn chuyển đến argparse.

Sau khi xóa tham số này, các đối số được chuyển bởi người dùng sẽ tự hiển thị, nhưng chương trình sẽ ném lỗi khi không có đối số nào được chuyển.

Thêm tiền tố '-' vào thông số đầu tiên giải quyết vấn đề này theo cách lười nhất.

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('--action', nargs='*', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args() 

Nhược điểm để phương pháp này là các tùy chọn thông qua bởi người sử dụng bây giờ phải được đi trước bởi '--trò', như:

app.py --action clear dump copy 
3

Bạn có thể kiểm tra xem người dùng sẽ được cung cấp hành động (trong trường hợp phân tích nó như là một yêu cầu, vị trí đối số), hoặc được cung cấp không có hành động (trong trường hợp phân tích nó như một đối số tùy chọn với mặc định):

import argparse 
import sys 

acts = ['clear', 'copy', 'dump', 'lock'] 
p = argparse.ArgumentParser() 
if sys.argv[1:]: 
    p.add_argument('action', nargs = '*', choices = acts) 
else: 
    p.add_argument('--action', default = ['dump', 'clear']) 

args = p.parse_args() 
print(args) 

khi chạy, mang lại những kết quả này:

% test.py 
Namespace(action=['dump', 'clear']) 
% test.py lock 
Namespace(action=['lock']) 
% test.py lock dump 
Namespace(action=['lock', 'dump']) 

Bạn có thể có lựa chọn khác để phân tích là tốt. Trong trường hợp đó, bạn có thể sử dụng parse_known_args để phân tích các tùy chọn khác, và sau đó xử lý các unknown đối số trong một lần chạy thứ hai:

import argparse 

acts = ['clear', 'copy', 'dump', 'lock'] 
p = argparse.ArgumentParser() 
p.add_argument('--foo') 
args, unknown = p.parse_known_args() 
if unknown: 
    p.add_argument('action', nargs = '*', choices = acts) 
else: 
    p.add_argument('--action', default = ['dump', 'clear']) 

p.parse_args(unknown, namespace = args) 
print(args) 

khi chạy, mang lại những kết quả này:

% test.py 
Namespace(action=['dump', 'clear'], foo=None) 
% test.py --foo bar 
Namespace(action=['dump', 'clear'], foo='bar') 
% test.py lock dump 
Namespace(action=['lock', 'dump'], foo=None) 
% test.py lock dump --foo bar 
Namespace(action=['lock', 'dump'], foo='bar') 
4

Trong tài liệu hướng dẫn (http://docs.python.org/dev/library/argparse.html#default), người ta nói:

Đối với đối số vị trí với nargs bằng? hoặc *, giá trị mặc định được sử dụng khi không có đối số dòng lệnh.

Sau đó, nếu chúng ta làm:

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', choices=acts, default='clear')  
print p.parse_args([]) 

Chúng tôi có được những gì chúng tôi mong đợi

Namespace(action='clear') 

Vấn đề là khi bạn đặt một danh sách như một mặc định. Nhưng tôi đã nhìn thấy nó trong doc,

parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!') 

Vì vậy, tôi không biết :-(

Nhưng dù sao, đây là một cách giải quyết mà công việc bạn muốn:

import sys, argparse 
acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', choices=acts) 
args = ['dump', 'clear'] # I set the default here ... 
if sys.argv[1:]: 
    args = p.parse_args() 
print args 
1

tôi đã kết thúc cách làm như sau:

  • không append
  • thêm danh sách trống để lựa chọn tốt nếu không thì đầu vào có sản phẩm nào phá vỡ
  • mà không mặc định
  • séc cho một danh sách trống sau đó và thiết lập mặc định thực tế trong trường hợp đó

Ví dụ:

parser = argparse.ArgumentParser() 
parser.add_argument(
    'is', 
    type=int, 
    choices=[[], 1, 2, 3], 
    nargs='*', 
) 

args = parser.parse_args(['1', '3']) 
assert args.a == [1, 3] 

args = parser.parse_args([]) 
assert args.a == [] 
if args.a == []: 
    args.a = [1, 2] 

args = parser.parse_args(['1', '4']) 
# Error: '4' is not valid. 
Các vấn đề liên quan