オプションの引数として辞書を設定しようとしています(argparseを使用)。次の行は私がこれまでに持っているものです:
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'}
正常に動作します。
では、代わりにどのように引数を渡す必要がありますか?前もって感謝します。
これを壊滅させる: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'}
完全を期すために、json.loadsと同様に、yaml.load(PyPIのPyYAMLから入手可能)を使用できます。これには、たとえば整数を文字列に強制するか、yaml変換セマンティクスを克服しようとしない限り、コマンドラインで個々のキーと値を引用する必要がないという点でjsonよりも優れています。ただし、文字列にはスペースが含まれているため、文字列全体を引用符で囲む必要があることは明らかです。
>>> 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がバーフィングするのを防ぐために、キーと値の多くは引用または言い換える必要があります。文字列値ではなく数値が必要な場合は、次のデータを使用すると非常にうまく機能するようです。
>>> 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}
辞書リテラルのようなものを引数パーサーに確実に取り込むことができますが、それを引用する必要があるため、シェルがコマンドラインを解析すると、次のようになります。
したがって、このようなものは、必要なテキストをプログラムに取り込むことができます。
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'}
私が見つけた最も簡単な方法の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'})
一般的なアドバイス:evalは使用しないでください。
本当にしなければならない場合... "eval"は危険です。悪意のある入力を故意に入力する人がいないことが確実な場合に使用します。それでも、不利な点があります。悪い例を1つ取り上げました。
ただし、json.loadsの代わりにevalを使用すると、いくつかの利点もあります。 dictは実際には有効な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'})
あなたは試すことができます:
$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}"
私は今、私の電話でこれをテストしていません。
編集:ところで私は@wimに同意します、引数としてdictの各kvを持っている方がユーザーにとってより良いと思います。
私は自分と同じようなことをしなければならなかったので、これは別の解決策です。 ast
モジュールを使用して、端末に文字列として入力される辞書をdictに変換します。とても簡単です。
次のようにtest.py
:
import argparse
import ast
parser = argparse.ArgumentParser()
parser.add_argument('--params', '--p', help='dict of params ',type=str)
options = parser.parse_args()
my_dict = options.params
my_dict = ast.literal_eval(my_dict)
print(my_dict)
for k in my_dict:
print(type(my_dict[k]))
print(k,my_dict[k])
次に、terminal/cmd行に次のように記述します。
python test.py --p '{"name": "Adam", "lr": 0.001, "betas": (0.9, 0.999)}'
{'name': 'Adam', 'lr': 0.001, 'betas': (0.9, 0.999)}
<class 'str'>
name Adam
<class 'float'>
lr 0.001
<class 'Tuple'>
betas (0.9, 0.999)