it-swarm.com.de

Übergeben einer Liste an Python von der Befehlszeile aus

Ich möchte, dass mein Python-Skript von der Befehlszeile aus ausgeführt wird, wenn es einige Argumente liefert. Eines der Argumente sollte jedoch eine Liste von Optionen sein, die für ein Segment des Skripts spezifisch sind. Wäre Stringsparsing die einzige Möglichkeit, dies zu tun, indem die Liste tatsächlich erstellt wird, nachdem die Zeichenfolge "Befehlszeilenliste" von Kommas getrennt wurde? Wenn ja, wie würden Sie das angehen? 

Beispiel: -details = ['name', 'title', 'address'] 

25
Sam

Programm:

import sys, ast, getopt, types

def main(argv):            
    arg_dict={}
    switches={'li':list,'di':dict,'tu':Tuple}
    singles=''.join([x[0]+':' for x in switches])
    long_form=[x+'=' for x in switches]
    d={x[0]+':':'--'+x for x in switches}
    try:            
        opts, args = getopt.getopt(argv, singles, long_form)
    except getopt.GetoptError:          
        print "bad arg"                       
        sys.exit(2)       

    for opt, arg in opts:        
        if opt[1]+':' in d: o=d[opt[1]+':'][2:]
        Elif opt in d.values(): o=opt[2:]
        else: o =''
        print opt, arg,o
        if o and arg:
            arg_dict[o]=ast.literal_eval(arg)

        if not o or not isinstance(arg_dict[o], switches[o]):    
            print opt, arg, " Error: bad arg"
            sys.exit(2)                 

    for e in arg_dict:
        print e, arg_dict[e], type(arg_dict[e])        

if __== '__main__':
    main(sys.argv[1:])        

Befehlszeile:

python py.py --l='[1,2,3,[1,2,3]]' -d "{1:'one',2:'two',3:'three'}" --tu='(1,2,3)'

Ausgabe:

args:  ['--l=[1,2,3,[1,2,3]]', '-d', "{1:'one',2:'two',3:'three'}", '--tu=(1,2,3)']
tu (1, 2, 3) <type 'Tuple'>
di {1: 'one', 2: 'two', 3: 'three'} <type 'dict'>
li [1, 2, 3, [1, 2, 3]] <type 'list'>

Dieses Code-Snippet benötigt kurze oder lange Befehlsschalter wie -l oder --li= und parst den Text nach dem Wechsel in eine Python-Datenstruktur wie eine Liste, ein Tuple oder ein Dikt. Die geparste Datenstruktur endet in einem Wörterbuch mit der langen Umschalttaste. 

Die Verwendung von ast.literal_eval ist relativ sicher. Es können nur Python-Datendefinitionen analysiert werden. 

28
the wolf

argparse ist schön dafür, es ist in der Standardbibliothek ab 2.7 und 3.2 aber sonst ein pip install weg.

Ihr Hauptanliegen bei der Angabe einer Liste mit variabler Länge kann durch die Interpretation der Liste als einzelnes Argument in der Shell unter Verwendung von Anführungszeichen gelöst werden (dies könnte von Ihrer Shell abhängen):

% python prog.py 'name title address' spam

wo Prog.py enthält

import sys
my_list = sys.argv[1].split() 
# my_list is ['name', 'title', 'address']
if 'name' in my_list:
   do_something()

oder ähnliches. Verwenden Sie ein Argument mit Split, um Ihre Liste einzugrenzen:

% python prog.py "you're a foo, lift the bar"

my_list = [x.strip() for x in  sys.argv[1].split(',')]
# my_list is ["you're a foo", "lift the bar"]

Bitte verwenden Sie stattdessen argparse; vor allem, wenn Sie -c-Stilflags verwenden möchten.

Eine Möglichkeit, Ihre Frage zu interpretieren, ist:

"Ich verwende bereits argparse, da dies die sinnvolle Art ist, Befehlszeilenargumente in Python zu interpretieren. Wie kann ich angeben, dass sich einige Optionen in einer bestimmten Kategorie befinden?"

In Ihrer Frage haben Sie ein Beispiel gezeigt, was die Muscheln, an denen ich arbeite, ersticken würden.

% python prog.py -v -details=['name', 'title', 'address'] --quickly -t 4

es wäre nicht möglich, Python zu analysieren, weil sie Leerzeichen verwenden, um Argumente zu trennen, und [und] möglicherweise als Shell-Syntax verwendet werden.

Ich schlage stattdessen folgendes vor

% python prog.py -v --details name title address --quickly -t 4

wo eine prog.py-Datei von

import argparse

parser = argparse.ArgumentParser() 
parser.add_argument('-v', action='store_true')
parser.add_argument('--details', nargs='*')
parser.add_argument('--quickly', action='store_true')
parser.add_argument('-t')

args = parser.parse_args()
#args is Namespace(details=['asdf', 'a', 'a'], quickly=False, t='4', v=True)
details = args.details
#details is ['asdf', 'a', 'a']

Nun, gemäß Ihrer Frage, mussten Sie die Zeichenfolge nicht selbst analysieren.

26
Thomas

Ja, argparse ist Ihre beste Wette, und wenn Sie einem der genannten Argumente eine Liste mit Werten bereitstellen möchten, sieht dies folgendermaßen aus (der nargs-Parameter ist der Schlüssel dazu):

>>> import argparse
>>> arg_parser = argparse.ArgumentParser()
>>> arg_parser.add_argument('--details',
                            nargs='*',
                            type=str,
                            default=[],
                            help='a list of the details')

# your args on the command line like this example
>>> the_args = arg_parser.parse_args("--details 'name' 'title' 'address'".split())
>>> print the_args.details
["'name'", "'title'", "'address'"])
3
Mark Gemmill

Mir gefällt der Ansatz von the-wolf sehr, wenn Sammlungen mit variabler Länge als explizite String-Argumente verwendet werden.

Meiner Meinung nach hat nargs='*' nennenswerte Nachteile: Wenn Sie versuchen, Zeichenfolgen als Positionsargumente zu sammeln (mindestens eine Zeichenfolge muss vorhanden sein), oder wenn Sie Subparser verwenden möchten, werden Sie feststellen, dass nargs='*' und nargs='+' greedy Completion verwenden Es scheint aus guten Gründen nicht aufzuhören zu konsumieren. Auch wenn die Syntax für ein optionales Argument oder eine Zahl angezeigt wird, wird der String () - Typ weiter verwendet. (Dies wird mit Subparsern schwieriger zu erwarten).

Im besten Fall werden nach (platzierte und optionale) Argumente ignoriert, und, schlimmer noch, Sie übergeben wahrscheinlich corrupt an das argparse-Array. 

Wir sollten in der Lage sein, einen benutzerdefinierten ActionType zu definieren, der nach einer Zeichenfolge in Anführungszeichen sucht. Wenn er einen findet, passen wir die Beispiele von the-wolf an (fast wörtlich). 

Dies hält die Dinge in Ordnung und macht die Verwendung von Variablensammlungen weniger zickig.

0
user2097818