2017-09-15 69 views
1

假設我有一個名爲myprog的程序,它將一些文件名作爲輸入,並且我還希望使用命令行參數爲每個文件設置打開模式。 例如如何處理與argparse有關係的命令行參數?

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

這意味着打開文件a.txt與模式r,文件b.txt沒有--mode阿根廷,所以用默認模式r打開它,該文件c.txt,使用a模式打開它。

+0

如果您使用的是pycharm,則可以使用設置指定命令行參數。 –

+0

那麼,問題是什麼? –

+0

你保證每個'--input'都有對應的'--mode'嗎?如果是這樣,那麼你可以在'input'和'mode'上使用'append''動作,然後''壓縮'列表。如果沒有(根據你的例子看起來就像是這種情況),那麼事情就會變得更加困難,除非你願意重組命令行 - 例如'myprog --input a.text r --input b.txt --input c.txt a ...' – mgilson

回答

4

這是一個棘手的問題,因爲argparse不會讓您知道哪個--input與某個--mode相關聯。您可以改變命令的結構,使文件名和模式是由標記字符分隔:

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

顯然,這假設你沒有文件後綴名爲:<mode>其中<mode>是任何可接受文件模式。如果這是一個OK結構,那麼就像編寫自定義操作或類型來解析字符串並返回合適的對象一樣簡單。例如

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

其他解決方案可能涉及使用nargs='*'然後解析出傳遞的參數列表。


最後,實現你所實際上要求不會有太大困難,我們需要做一個假設。假設​​將從左到右解析項目。鑑於圖書館的功能,這是唯一合理的選擇實施,據我所知...

鑑於實施,我們可以做到這一點與自定義類型和自定義Action。該類型只是一個結構,可以將filenamemode分組在一起。每當我們點擊一​​個--input並將它追加到一個列表中(這是支持開箱的​​),我們將使用​​構造這種類型的新實例。接下來,我們將編寫一個自定義操作,以在每次插入--mode參數時更新列表中最後一個「文件結構」的mode

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

我選擇拋出一個錯誤,如果--mode任何--input之前顯示出來,但如果2 --mode遵循--input,我只是覆蓋以前的值。如果您想要進行更多錯誤檢查,那麼在FileInfo類中編寫更多代碼以確保在您更新模式時尚未設置任何模式。

+0

是的,''parse_args'遍歷'argv'('sys.argv [1:]'),或者處理定位和選擇權。各自的'nargs'控制給每個'Action'分配多少個字符串。所以你的自定義Action類應該像廣告一樣工作。 – hpaulj

0

如果在命令行是這樣的:

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

這是確定添加一些像這樣的代碼:

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) 

然後你就可以分析參數對象,args_dict變量。值是這樣的:

$ 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']} 

可以遍歷都「輸入」鍵和「模式」在args_dict變量鍵,爲保持輸入列表中(它的「的test2.txt」這裏),你可以用'r'模式打開它。

但是,如果您的命令行必須寫類似:

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

我不認爲這是容易解析帶「R」模式下的b.txt,因爲argparse不知道哪個模式結合相對輸入...


獲得從@mgilson靈感的意見和答案,我已經找到了另一種方式來定義Action子類,使‘模式’輸入有用。

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) 

而且客戶端代碼可以是這樣的:

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) 

然後我們可以分析參數對象,args_dict變量更容易。如果在命令行是這樣的:

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

結果將是:

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

在其他特殊的方式,如果命令行是這樣的:

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

結果將是:

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

而且n您可以更輕鬆地解析dict,輸入參數中的'test2.txt〜test4.txt'將具有默認'r'模式:)

+0

這也是我的第一個想法,但考慮一點之後,這絕對是可能的(甚至令人驚訝的是,甚至沒有那麼難)。由於'argparse'提供了編寫自己的動作的鉤子,因此可以通過自定義動作對狀態機進行編碼,而沒有太多困難。看到我的回答:-) – mgilson

+0

@mgilson非常感謝您的評論:)解決這個問題顯然是很好的一點。但它使得parse_args的*'mode'*值無用。但我認爲這不是一個很大的問題。 – Ballack