2011-10-02 108 views
15

我想設置一個字典作爲可選參數(使用argparse);下面一行是我到目前爲止有:type = dict in argparse.add_argument()

parser.add_argument('-i','--image', type=dict, help='Generate an image map from the input file (syntax: {\'name\': <name>, \'voids\': \'#08080808\', \'0\': \'#00ff00ff\', \'100%%\': \'#ff00ff00\'}).') 

但在運行腳本:

$ ./script.py -i {'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'} 

script.py: error: argument -i/--image: invalid dict value: '{name:' 

即使,解釋裏面,

>>> a={'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'} 

的作品就好了。

那麼我應該如何傳遞參數呢? 在此先感謝。

+0

您可以從外部文件或標準輸入讀取的格式,如JSON,然後解析它。所以你argparse類型實際上是一個文件。 –

+0

@wim在他的回答中說shell在將它們傳遞給python之前正在處理這些參數。如果你用'echo'('echo ./script.py -i {'name':...'')你會看到什麼python看到(主要是它沒有收到任何報價)。在你的情況下,你的param中沒有'$'(可以被shell解釋爲一個環境變量),你可以用雙引號括住你的dict:'./script.py -i「{'name':' img.png',....}「' –

回答

4

我敢打賭,你的殼是福> < 0ring大括號(見here)。我只需在CLI中使用參數Argument groups逐個傳遞參數,然後以編程方式構建字典。

傳遞一個像字典這樣複雜的python對象,迫使用戶知道python語法,對我來說似乎有點讓人難過。

0

你可以嘗試:

$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}" 

我沒有測試過這一點,我的手機上現在。

編輯: BTW我同意@wim,我覺得具有字典的每個KV作爲一個參數將是用戶更好的。

1

可以在東西看起來像字面字典入參數解析器肯定搞定,但你因此當外殼解析您的命令行引用它,它就會出現,

  • 單參數,而不是很多(空格字符是正常的參數分隔符)
  • 正確引用(殼解析過程中刪除引號,因爲它使用它們分組)

因此,像這樣可以得到你想要的文字進入你的p rogram:

python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}" 

但是,這個字符串不是dict構造函數的有效參數;相反,它是一個有效的Python代碼片段。你可以告訴你的論點解析器的這種說法的「類型」爲eval,並且將工作:

import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument('-i','--image', type=eval, help='Generate an image map...') 
args = parser.parse_args() 
print args 

,並調用它:

% python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}" 
Namespace(image={'0': '#ff00ff00', '100%': '#f80654ff', 'voids': '#00ff00ff', 'name': 'img.png'}) 

但是,這不是安全的;輸入可能是任何東西,並且您正在評估任意代碼。這將是同樣笨重,但下面會更安全:

import argparse 
import ast 

parser = argparse.ArgumentParser() 
parser.add_argument('-i','--image', type=ast.literal_eval, help='Generate an image map...') 
args = parser.parse_args() 
print args 

這也適用,但許多關於什麼將允許eval「d更加嚴格。

儘管如此,讓用戶鍵入一些合適的引用,看起來像命令行上的python字典是非常困難的。而且,你必須在事實之後進行一些檢查,以確保它們傳遞到字典中,而不是其他功能,並且在其中有正確的鍵。非常容易使用,如果:

import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument("--image-name", required=True) 
parser.add_argument("--void-color", required=True) 
parser.add_argument("--zero-color", required=True) 
parser.add_argument("--full-color", required=True) 

args = parser.parse_args() 

image = { 
    "name": args.image_name, 
    "voids": args.void_color, 
    "0%": args.zero_color, 
    "100%": args.full_color 
    } 
print image 

爲:

% python MYSCRIPT.py --image-name img.png --void-color \#00ff00ff --zero-color \#ff00ff00 --full-color \#f80654ff 
{'100%': '#f80654ff', 'voids': '#00ff00ff', 'name': 'img.png', '0%': '#ff00ff00'} 
+0

哇,感謝您的可能性概述;然而,儘管在這個例子中我只寫了0和100%,但它們實際上可以是任何值(例如{'46%':'#0f0e0d0c','3629','#f0e0d0c0'})在你最後一段代碼中...... – user975296

37

Necroing這樣:json.loads也在這裏工作。它看起來不太髒。

import json 
import argparse 

test = '{"name": "img.png","voids": "#00ff00ff","0": "#ff00ff00","100%": "#f80654ff"}' 

parser = argparse.ArgumentParser() 
parser.add_argument('-i', '--input', type=json.loads) 

args = parser.parse_args(['-i', test]) 

print(args.input) 

返回:

{u'0': u'#ff00ff00', u'100%': u'#f80654ff', u'voids': u'#00ff00ff', u'name': u'img.png'}

+0

'json.loads'是'type'的不錯選擇。就像'int'和'float'一樣,它需要一個字符串,並且如果它無法處理則返回一個'ValueError'。它比'eval'更安全。爲此,它可能有點過於籠統(即它可以處理一個''[1,2]'')列表,但是用戶可以在'parse_args()'後處理。 – hpaulj

+0

當這些值是'str'ings,'int','float'時,它工作正常。對於其他類型的值,例如'bool',它不會(但是將'1'傳遞給'True'應該可以工作,但所有代碼都是正確的)。 – gerrit

1

有一個問題我已經找到了最簡單的方法是解析字典作爲一個列表,然後將其轉換成一個字典。例如使用Python3:

#!/usr/bin/env python3 
import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument('-i', '--image', type=str, nargs='+') 
args = parser.parse_args() 
if args.image is not None: 
    i = iter(args.image) 
    args.image = dict(zip(i, i)) 
print(args) 

那麼你可以鍵入命令行類似:

./script.py -i name img.png voids '#00ff00ff' 0 '#ff00ff00' '100%' '#f80654ff' 

,以獲得期望的結果:

Namespace(image={'name': 'img.png', '0': '#ff00ff00', 'voids': '#00ff00ff', '100%': '#f80654ff'}) 
7

爲了完整,同樣以JSON .loads,你可以使用yaml.load(可從PyPI的PyYAML獲得)。這比json的優勢在於,除非您試圖將整數強制爲字符串或以其他方式克服yaml轉換語義,否則不需要在命令行中引用個別鍵和值。但顯然整個字符串需要引用,因爲它包含空格!

>>> import argparse 
>>> import yaml 
>>> parser = argparse.ArgumentParser() 
>>> parser.add_argument('-fna', '--filename-arguments', type=yaml.load) 
>>> data = "{location: warehouse A, site: Gloucester Business Village}" 
>>> ans = parser.parse_args(['-fna', data]) 
>>> print ans.filename_arguments['site'] 
Gloucester Business Village 

儘管在給出的問題中承認,許多鍵和值必須引用或改寫,以防止yaml barfing。如果您需要數字而不是字符串值,則使用以下數據似乎可以很好地工作:

>>> parser.add_argument('-i', '--image', type=yaml.load) 
>>> data = "{name: img.png, voids: 0x00ff00ff, '0%': 0xff00ff00, '100%': 0xf80654ff}" 
>>> ans = parser.parse_args(['-i', data]) 
>>> print ans.image 
{'100%': 4161164543L, 'voids': 16711935, 'name': 'img.png', '0%': 4278255360L} 
+0

更正(缺少)parser.parse_args調用。感謝您指出這一點,Hotschke – Hamish

0

一般建議:請勿使用eval。

如果你真的要... 「eval」是危險的。如果您確定沒有人會故意輸入惡意輸入,請使用它。即使這樣也會有缺點。我列舉了一個不好的例子。

雖然使用eval代替json.loads也有一些優點。字典並不需要是有效的json。因此,eval在接受「字典」時可能相當寬鬆。我們可以通過確保最終結果確實是一個Python字典來照顧「危險」部分。

import json 
import argparse 

tests = [ 
    '{"name": "img.png","voids": "#00ff00ff","0": "#ff00ff00","100%": "#f80654ff"}', 
    '{"a": 1}', 
    "{'b':1}", 
    "{'$abc': '$123'}", 
    '{"a": "a" "b"}' # Bad dictionary but still accepted by eval 
] 
def eval_json(x): 
    dicti = eval(x) 
    assert isinstance(dicti, dict) 
    return dicti 

parser = argparse.ArgumentParser() 
parser.add_argument('-i', '--input', type=eval_json) 
for test in tests: 
    args = parser.parse_args(['-i', test]) 
    print(args) 

輸出:

Namespace(input={'name': 'img.png', '0': '#ff00ff00', '100%': '#f80654ff', 'voids': '#00ff00ff'}) 
Namespace(input={'a': 1}) 
Namespace(input={'b': 1}) 
Namespace(input={'$abc': '$123'}) 
Namespace(input={'a': 'ab'}) 
+0

這是非常危險的建議。使用eval將(顯然)導致來自cmdline的輸入被評估爲python。這發生在*它返回值之前,所以你的類型檢查太遲了。此外,還有大量有效的危險python,它仍然會返回一個字典...... 「我們可以照顧危險」聲明既不準確又危險,可以作爲建議傳播。 –

+0

好的。同意。我正在改變答案的語言。 –