Type = dict в argparse.add_argument()
Я пытаюсь настроить словарь как необязательный аргумент (используя 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:
$ ./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'}
отлично работает.
Итак, как мне передать аргумент?
Спасибо заранее.
Ответы
Ответ 1
Отключить это: 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'}
Ответ 2
Для полноты и аналогично json.loads вы можете использовать yaml.load(доступный от PyYAML в PyPI). Это имеет преимущество перед 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
Хотя, по общему признанию, в заданном вопросе многие ключи и значения должны были бы быть указаны или перефразированы, чтобы предотвратить ямль от барфинга. Использование следующих данных, похоже, работает довольно хорошо, если вам нужны числовые, а не строковые значения:
>>> 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}
Ответ 3
Держу пари, что ваша оболочка работает с фигурными скобками, поскольку фигурные скобки - это синтаксис, используемый для функций расширения фигурных скобок во многих оболочках (см. здесь).
Передача в сложный контейнер, такой как словарь, требующий, чтобы пользователь знал синтаксис Python, кажется плохим выбором дизайна в интерфейсе командной строки. Вместо этого я бы рекомендовал просто передавать параметры по одному в CLI в группе аргументов, а затем программно формировать dict из проанализированной группы.
Ответ 4
Вы можете определенно получить что-то похожее на литерал словаря в парсер для аргументов, но вы должны его процитировать, поэтому, когда оболочка анализирует вашу командную строку, она входит как
- один аргумент вместо многих (символ пробела является ограничителем нормальных аргументов)
- правильно цитируется (оболочка удаляет кавычки во время разбора, потому что использует их для группировки)
Итак, что-то вроде этого может получить текст, который вы хотите в свою программу:
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'}
Ответ 5
Один из самых простых способов, который я нашел, - это проанализировать словарь как список, а затем преобразовать его в словарь. Например, используя 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'})
Ответ 6
Общий совет: НЕ ИСПОЛЬЗУЙТЕ 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'})
Ответ 7
Вы можете попробовать:
$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}"
Я не тестировал это прямо сейчас на своем телефоне.
Изменить: Кстати. Я согласен с @wim, я думаю, что каждый kv из dict в качестве аргумента будет приятнее для пользователя.
Ответ 8
Вот еще одно решение, так как я должен был сделать что-то подобное сам. Я использую модуль 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])
Затем в строке терминала /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)