Pytanie w sprawie arguments, parameters, argparse, python – python, argparse: włącz parametr wejściowy, gdy określono inny

12

W moim skrypcie Pythona chcę móc użyć opcjonalnego parametru wejściowegotylko gdy określono inny opcjonalny parametr. Przykład:

$ python myScript.py --parameter1 value1
$ python myScript.py --parameter1 value1 --parameter2 value2

Ale nie:

$ python myScript.py --parameter2 value2

Jak to zrobić za pomocą argparse?

Dzięki!

Co chcesz zrobić, jeślimyScript.py --parameter2 value2 --parameter1 value1? Jest to bardzo trudny przypadek, jeśli chcesz go przekazać. mgilson
bugs.python.org/issue11588 to prośba o błąd dlamutually_inclusive_group cecha. To nie zadziałałoby w tym przypadku--parameter1 może wystąpić bez--parameter2. Przyczyń się do tego problemu, jeśli masz pomysły na to, jak to lub powiązane ograniczenia mogą zostać wdrożone. hpaulj

Twoja odpowiedź

3   odpowiedź
2

Możesz użyćparse_known_args () sprawdzić, czy--parameter1 zostało podane przed dodaniem--parameter2 do parsera.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--parameter1', dest='p1')
args = parser.parse_known_args()[0]
if args.p1:
  parser.add_argument('--parameter2', dest='p2')
args = parser.parse_args()

if args.p1:
  print args.p1
if args.p2:
  print args.p2

Wyniki:

$ python myScript.py --parameter1 a
a
$ python myScript.py --parameter1 a --parameter2 b
a
b
$ python myScript.py --parameter2 b
usage: myScript.py [-h] [--parameter1 P1]
myScript.py: error: unrecognized arguments: --parameter2 b
--help będzie jednak niedokładny. pmav99
2

Jednym z pomysłów byłoby ręczne sprawdzenie wyjścia wywołaniaparser.parse_args() i zgłosić wyjątek lub w inny sposób zawieść, jeśli--parameter2 jest używany, ale--parameter1 nie jest.

Możesz także spróbować ustawić aakcja niestandardowa (przewiń w dół) do--parameter2 sprawdź czy--parameter1 istnieje wewnątrz przestrzeni nazw.

Wreszcie możesz spróbować użyćpodstrony, chociaż nie wiem, czy pozwoli ci to określić wartość--parameter1.

10

Użyj akcji niestandardowej:

import argparse

foo_default=None    

class BarAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        didfoo=getattr(namespace,'foo',foo_default)
        if(didfoo == foo_default):
            parser.error( "foo before bar!")
        else:
            setattr(namespace,self.dest,values)

parser=argparse.ArgumentParser()
parser.add_argument('--foo',default=foo_default)
parser.add_argument('--bar',action=BarAction,help="Only use this if --foo is set")

#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())

Można to nawet zrobić w nieco łatwiejszy sposób, jeśli nie masz nic przeciwko nieudokumentowanemu działaniu argparse:

import argparse

parser=argparse.ArgumentParser()
first_action=parser.add_argument('--foo',dest='cat',default=None)

class BarAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        didfoo=getattr(namespace,first_action.dest,first_action.default)
        if(didfoo == first_action.default):
            parser.error( "foo before bar!")
        else:
            setattr(namespace,self.dest,values)

parser.add_argument('--bar',action=BarAction,
                    help="Only use this if --foo is set")

#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())

W tym przykładzie otrzymujemy wartość domyślnąfoo i jego miejsce docelowe z obiektu akcji zwróconego przezadd_argument (Wartość zwracana przez add_argument nie jest nigdzie udokumentowana). Jest to wciąż trochę kruche (jeśli chcesz określić atype= słowo kluczowe do--foo argument na przykład).

Wreszcie możesz sprawdzićsys.argv przed analizowaniem.

import sys
if ("--parameter2" in sys.argv) and ("--parameter1" not in sys.argv):
    parser.error("parameter1 must be given if parameter2 is given")

Robi się to trochę trudniejsze, jeśli--parameter1 może również zostać wywołany przez--p1, ale masz pomysł. Wtedy możesz użyć

if (set(sys.argv).intersection(('--p2',...)) and 
    not set(sys.argv).intersection(('--p1',...)))

Zaletą jest to, że nie wymaga żadnej szczególnej kolejności. (--p2 nie musi podążać--p1 w linii poleceń). I tak jak poprzednio, możesz uzyskać listę ciągów poleceń, które będą uruchamiać określoną akcję za pośrednictwemoption_strings atrybut zwrócony przezparser.add_argument(...). na przykład

import argparse
import sys   
parser=argparse.ArgumentParser()
action1=parser.add_argument('--foo')
action2=parser.add_argument('--bar',
                            help="Only use this if --foo is set")

argv=set(sys.argv)
if (( argv & set(action2.option_strings) ) and 
      not ( argv & set(action1.option_strings) )):
                #^ set intersection
     parser.error(' or '.join(action1.option_strings)+
                  ' must be given with '+
                  ' or '.join(action2.option_strings))

Powiązane pytania