Frage an arguments, argparse, python, parameters – python, argparse: Aktiviert den Eingabeparameter, wenn ein anderer angegeben wurde

12

In meinem Python-Skript möchte ich einen optionalen Eingabeparameter verwenden könnennur wenn ein anderer optionaler Parameter angegeben wurde. Beispiel:

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

Aber nicht:

$ python myScript.py --parameter2 value2

Wie mache ich das mit argparse?

Vielen Dank!

Was soll passieren, wennmyScript.py --parameter2 value2 --parameter1 value1? Dies ist ein sehr schwieriger Fall, wenn Sie möchten, dass er bestanden wird. mgilson
bugs.python.org/issue11588 ist eine Fehleranfrage für amutually_inclusive_group Merkmal. Das würde in diesem Fall nicht ganz funktionieren, da--parameter1 kann ohne auftreten--parameter2. Tragen Sie zu diesem Thema bei, wenn Sie Ideen haben, wie diese oder verwandte Einschränkungen implementiert werden könnten. hpaulj

Deine Antwort

3   die antwort
2

parser.parse_args() und eine Ausnahme auslösen oder anderweitig fehlschlagen, wenn--parameter2 wird aber verwendet--parameter1 ist nicht.

Sie können auch versuchen, a einzustellenbenutzerdefinierte Aktion (etwas runterscrollen) bis--parameter2 überprüfe ob--parameter1 existiert innerhalb des Namespace.

Schließlich könnten Sie versuchen, mitSubparser, obwohl ich nicht weiß, ob Sie einen Wert für angeben können--parameter1.

10

Verwenden Sie eine benutzerdefinierte Aktion:

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

Dies kann sogar auf eine etwas einfacher zu wartende Art und Weise durchgeführt werden, wenn Sie sich auf ein undokumentiertes Verhalten von argparse verlassen können:

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

In diesem Beispiel erhalten wir die Standardeinstellung fürfoo und es ist das Ziel des von zurückgegebenen Aktionsobjektsadd_argument (Der Rückgabewert von add_argument ist nirgendwo dokumentiert, wo ich ihn finden kann). Dies ist immer noch ein wenig fragil (Wenn Sie eine angeben möchtentype= Schlüsselwort zum--foo Argument zum Beispiel).

Schließlich können Sie überprüfensys.argv vor dem Parsen.

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

Das wird etwas kniffliger, wenn--parameter1 könnte auch ausgelöst werden durch--p1, aber du kommst auf die Idee. Dann könntest du verwenden

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

Der Vorteil hierbei ist, dass keine bestimmte Reihenfolge erforderlich ist. (--p2 muss nicht folgen--p1 auf der Kommandozeile). Und nach wie vor können Sie die Liste der Befehlszeichenfolgen abrufen, die Ihre bestimmte Aktion über die auslösenoption_strings Attribut zurückgegeben vonparser.add_argument(...). z.B.

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

Sie können verwendenparse_known_args () um zu überprüfen, ob--parameter1 wurde vor dem Hinzufügen gegeben--parameter2 zum Parser.

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

Ergebnisse:

$ 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 wird aber ungenau sein. pmav99

Verwandte Fragen