2017-09-15 3 views
1

Angenommen, ich habe ein Programm namens myprog, das einen Dateinamen als Eingabe verwendet, und ich möchte auch Befehlszeilenargumente verwenden, um den offenen Modus für jede Datei festzulegen. Zum BeispielWie werden Befehlszeilenargumente behandelt, die Beziehungen zu argparse haben?

myprog --input a.txt --mode r --input b.txt --input c.txt --mode a 

Welche offene Datei bedeutet a.txt mit Modus r, Datei b.txt keine --mode arg, haben so öffnen Sie sie mit Standard-Modus r, und für die Datei c.txt, verwenden Sie den a Modus öffne es.

+0

Wenn Sie pycharm verwenden, können Sie Befehlszeilenargumente mit Einstellungen angeben. –

+0

Also, was ist die Frage? –

+0

Gewährleisten Sie, dass für jeden "- Eingang" ein entsprechender '- Modus' vorliegt? Wenn ja, dann könntest du einfach die 'append'-Aktion sowohl auf" input "als auch auf" mode "anwenden und dann die Listen" zippen ". Wenn nicht (wie es in Ihrem Fall der Fall ist), dann werden die Dinge viel schwieriger, wenn Sie nicht bereit sind, die Befehlszeile neu zu strukturieren - z. 'myprog --input a.text r --input b.txt --input c.txt a ...' – mgilson

Antwort

4

Dies ist ein kniffliges Problem, weil argparse Ihnen keine Möglichkeit gibt zu wissen, welche --input einer bestimmten --mode zugeordnet ist. Sie konnte die Struktur des Befehls ändern, so dass der Dateiname und der Modus durch einen Sentinel-Zeichen getrennt sind:

myprog --input a.txt:r --input b.txt --input c.txt:a 

Offensichtlich Sie geht davon haben keine Dateien, deren Namen in :<mode> enden, wo <mode> jede akzeptabel ist Dateimodus. Wenn dies eine OK-Struktur ist, wird dies so einfach wie das Schreiben einer benutzerdefinierten Aktion oder eines benutzerdefinierten Typs, um die Zeichenfolge zu analysieren und ein geeignetes Objekt zurückzugeben. z.B.

def parse_fstr(s): 
    filename, _, mode = s.rpartition(':') 
    return (filename, mode or 'r') 

Andere Lösungen könnten beinhalten nargs='*' und anschließend die Liste der Argumente übergeben Parsen aus.


schließlich zu implementieren, was Sie haben tatsächlich für ohne allzu große Schwierigkeiten gefragt, müssen wir eine Annahme machen. Die Annahme ist, dass argparse Elemente von links nach rechts analysieren wird. Angesichts der Funktionalität der Bibliothek, das ist die einzige vernünftige Wahl für die Umsetzung, soweit ich das beurteilen kann ...

Angesichts dieser Implementierung können wir dies mit einem benutzerdefinierten Typ und eine benutzerdefinierte Action tun. Der Typ ist einfach eine Struktur, um eine filename und eine mode zusammen gruppiert zu halten. Wir werden argparse verwenden, um eine neue Instanz dieses Typs jedes Mal zu erstellen, wenn wir eine --input treffen und an eine Liste anhängen (Dies wird standardmäßig von argparse unterstützt). Als Nächstes schreiben wir eine benutzerdefinierte Aktion, um die mode der letzten "Dateistruktur" in der Liste jedes Mal zu aktualisieren, wenn wir auf ein --mode-Argument stoßen.

import argparse 


class FileInfo(object): 
    def __init__(self, name, mode='r'): 
     self.name = name 
     self.mode = mode 

    def __repr__(self): 
     return 'FileInfo(name={!r}, mode={!r})'.format(self.name, self.mode) 


class UpdateMode(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     try: 
      last_file_info = namespace.input[-1] 
     except IndexError: 
      # No file-info added yet. Error. 
      parser.error('{} must come after an --input'.format(option_string or '--mode')) 

     last_file_info.mode = values 


parser = argparse.ArgumentParser() 
parser.add_argument('--input', action='append', type=FileInfo) 
parser.add_argument('--mode', action=UpdateMode) 
print(parser.parse_args()) 

IIIIIIVV einen Fehler zu werfen, wenn --mode zeigt sich vor jeder --input, aber wenn 2 --mode ein --input folgen, ich bin überschreiben nur den vorherigen Wert. Wenn Sie mehr Fehler überprüfen möchten, ist es eine einfache Sache, ein wenig mehr Code in der FileInfo Klasse zu schreiben, um sicherzustellen, dass kein Modus bereits eingestellt wurde, wenn Sie den Modus aktualisieren.

+0

Ja, 'parse_args' iteriert alternativ durch' argv' ('sys.argv [1:]') Umgang mit Positionalen und Optionalen. Die jeweiligen "nargs" steuern, wie viele Strings zu jeder "Action" gegeben werden. Ihre benutzerdefinierten Aktionsklassen sollten also wie angekündigt funktionieren. – hpaulj

0

Wenn die Befehlszeile ist wie folgt:

myprog --input a.txt --mode r --input c.txt --mode a --input b.txt 

Es ist in Ordnung, einige Code wie folgt hinzuzufügen:

import argparse 

parser = argparser.ArgumentParser() 
parser.add_argument('--input', action='append') 
parser.add_argument('--mode', action='append') 
args = parser.parse_args() 
args_dict = vars(args) 

Dann können Sie die args analysieren Objekt, args_dict variabel.Der Wert ist wie folgt:

$ python test.py --input test.txt --mode w --input test3.txt --input test2.txt --mode a 
{'mode': ['w', 'a'], 'input': ['test.txt', 'test3.txt', 'test2.txt']} 

Sie beide laufen können ‚Eingang‘ Schlüssel und ‚mode‘ Schlüssel in den args_dict Variable, für die weiterhin die Eingabeliste (es ist ‚test2.txt‘ hier), können Sie es mit 'r' Modus öffnen.

Aber wenn Ihre Befehlszeile so etwas wie zu schreiben:

myprog --input a.txt --mode r --input b.txt --input c.txt --mode a 

Ich glaube nicht, es ist einfach, die b.txt mit ‚r‘ Modus zu analysieren, weil argparse nicht wissen, welchen Modus binde an den relativen Eingang ...


lassen Sie sich inspiriert von @mgilson ‚s Kommentar und Antwort, ich habe eine andere Art und Weise zu definieren Aktion Unterklasse gefunden, und macht die ‚mode‘ Eingang nützlich.

class ExtendReadOnlyAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     inputs = namespace.input 
     modes = getattr(namespace, self.dest) 
     if modes is None: 
      modes = [] 
     modes.extend(['r' for i in range(len(inputs) - len(modes))]) 
     modes[-1] = values 
     setattr(namespace, self.dest, modes) 

Und der Client-Code so etwas wie das sein kann:

import argparse 

parser = argparser.ArgumentParser() 
parser.add_argument('--input', action='append') 
parser.add_argument('--mode', action=ExtendReadOnlyAction) 
args = parser.parse_args() 
args_dict = vars(args) 

Dann können wir die args Objekt analysieren, mehr args_dict Variable einfacher. Wenn die Befehlszeile ist wie folgt:

$ python test.py --input test.txt --mode w --input test2.txt --input test3.txt --mode a 

Das Ergebnis wird sein:

{'mode': ['w', 'r', 'a'], 'input': ['test.txt', 'test2.txt', 'test3.txt']} 

Im besonderen Art und Weise, wenn die Befehlszeile wie folgt lautet:

$ python test.py --input test.txt --mode w --input test2.txt --input test3.txt --input test4.txt 

Das Ergebnis wird sein:

{'input': ['test.txt', 'test2.txt', 'test3.txt', 'test4.txt'], 'mode': ['w']} 

Und die Wenn Sie das dict einfacher parsen können, wird die 'test2.txt ~ test4.txt' im Eingabeargument den Standardmodus 'r' haben :)

+0

Das war auch mein erster Gedanke, aber nachdem ich ein wenig darüber nachgedacht habe, ist es definitiv möglich (und überraschenderweise nicht einmal so schwer). Da 'argparse' Hooks zum Schreiben eigener Aktionen bietet, können Sie eine Zustandsmaschine über benutzerdefinierte Aktionen ohne große Schwierigkeiten programmieren. Siehe meine Antwort :-) – mgilson

+0

@mgilson Sehr zu schätzen für deinen Kommentar:) Es ist klar und guter Punkt, dies zu lösen..Aber es macht den * 'mode' * Wert von parse_args unbrauchbar. Aber das ist kein sehr großes Problem, denke ich. – Ballack

Verwandte Themen