2011-12-15 111 views
17

我有一個腳本,我向用戶詢問要執行的預定義操作的列表。我還希望能夠在用戶沒有定義任何內容時採取特定的操作列表。然而,似乎試圖將這兩者結合在一起是不可能的。python argparse - 帶選項的可選append參數

當用戶沒有給出參數,他們收到一個錯誤,默認的選擇是無效

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') 

當他們這樣做定義一組動作,得到的命名空間擁有了用戶的操作追加到默認,而不是替換默認

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

我不確定這是否已被類似的bug解決了報告:http://bugs.python.org/issue9625。處理這種情況的一種可能方法是使用自定義操作,而不是使用`choices`關鍵字。看看[這個問題]上接受的答案(http://stackoverflow.com/questions/4194948/python-argparse-is-there-a-way-to-specify-a-range-in-nargs) – Chris 2011-12-15 22:04:32

回答

15

你需要什麼可以使用來進行定製argparse.Action如下面的例子:

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']) 

腳本的輸出是:

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

的行動被追加,因爲「行動=‘追加’」你傳遞的參數來argparse。

刪除此參數後,用戶傳遞的參數將自行顯示,但當沒有參數傳遞時,程序會引發錯誤。

爲第一個參數添加一個' - '前綴以最懶散的方式解決了這個問題。

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

對這個缺點的方法是,用戶通過該選項必須現在「--action」前面,如:

app.py --action clear dump copy 
3

您可以測試用戶是否提供行動(在這種情況下,解析它作爲一個需要,位置參數),或不供給動作(在這種情況下,解析它與默認可選參數):

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) 

運行時,產生以下結果:

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

你可能有其他選項解析爲好。在這種情況下,你可以使用parse_known_args來解析其他選項,然後處理unknown參數在第二遍:

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) 

運行時,產生這些結果:

% 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

在文檔(http://docs.python.org/dev/library/argparse.html#default),這是說:

對於NARGS等於位置參數?或*,當沒有命令行參數時,使用默認值。

然後,如果我們這樣做:

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

我們得到了我們所期望的

Namespace(action='clear') 

問題是,當你把一個表作爲默認值。 但我在doc看到它,

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

所以,我不知道:-(

總之,這裏是一個解決辦法,你想要做的工作:

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

最後我做了以下內容:

  • 沒有append
  • 加空單可能的選擇,否則空輸入打破
  • 沒有默認
  • 檢查空單後,並設置實際的默認在這種情況下

例子:

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. 
相關問題