web-dev-qa-db-ja.com

argparse.add_argument()のtype = dict

オプションの引数として辞書を設定しようとしています(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'}

正常に動作します。

では、代わりにどのように引数を渡す必要がありますか?前もって感謝します。

27
user975296

これを壊滅させる: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'}

48
Edd

完全を期すために、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}
9
Hamish

中括弧は多くのシェルで中括弧拡張機能に使用される構文であるため、シェルが中括弧をいじっているに違いありません( ここ を参照)。

辞書などの複雑なコンテナを渡すと、ユーザーはPython構文を知っている必要がありますが、コマンドラインインターフェイスでは設計上の選択としては不適切なようです。代わりに、オプションを1つで渡すことをお勧めします- 引数グループ 内のCLIで1つずつ実行し、解析されたグループからプログラムで辞書を作成します。

5
wim

辞書リテラルのようなものを引数パーサーに確実に取り込むことができますが、それを引用する必要があるため、シェルがコマンドラインを解析すると、次のようになります。

  • 多くではなく単一の引数(スペース文字は通常の引数区切り文字です)
  • 適切に引用されています(シェルは引用符をグループ化に使用しているため、解析中に引用符を削除します)

したがって、このようなものは、必要なテキストをプログラムに取り込むことができます。

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'}
3
Matt Anderson

私が見つけた最も簡単な方法の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'})
1
Juan A. Navarro

一般的なアドバイス: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'})
1
Mayank Jaiswal

あなたは試すことができます:

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

私は今、私の電話でこれをテストしていません。

編集:ところで私は@wimに同意します、引数としてdictの各kvを持っている方がユーザーにとってより良いと思います。

0
John Keyes

私は自分と同じようなことをしなければならなかったので、これは別の解決策です。 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)
0
Bradley