type = dict в argparse.add_argument () - PullRequest
       5

type = dict в argparse.add_argument ()

22 голосов
/ 02 октября 2011

Я пытаюсь установить словарь в качестве необязательного аргумента (используя 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'}

работает просто отлично.

Так как мне вместо этого передать аргумент? Заранее спасибо.

Ответы [ 7 ]

46 голосов
/ 02 августа 2013

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

8 голосов
/ 10 декабря 2013

Для полноты и аналогично 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

Хотя, по общему признанию в данном вопросе, многие ключи и значения должны быть заключены в кавычки или перефразированы, чтобы не допустить искажения 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}
4 голосов
/ 02 октября 2011

Могу поспорить, что ваша оболочка fu> <0 в скобках (см. <a href="http://www.linuxjournal.com/content/bash-brace-expansion" rel="nofollow"> здесь ). Я просто передавал бы аргументы один за другим в CLI, используя Группы аргументов , и создавал диктат программно.

Передача такого сложного объекта Python, как словарь, заставляющего пользователя знать синтаксис Python, кажется мне немного странной.

3 голосов
/ 02 октября 2011

Вы можете определенно добавить что-то, похожее на буквальный литерал, в анализатор аргументов, но вы должны заключить его в кавычки, поэтому, когда оболочка анализирует вашу командную строку, она появляется как

  • один аргумент вместо многих (символ пробела является нормальным разделителем аргумента)
  • правильно цитируется (оболочка удаляет кавычки во время синтаксического анализа, потому что использует их для группировки)

Так что-то вроде этого может получить желаемый текст в вашу программу:

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 голос
/ 06 сентября 2016

Общие советы: НЕ ИСПОЛЬЗУЙТЕ 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'})
1 голос
/ 05 декабря 2013

Один из самых простых способов, которые я нашел, - проанализировать словарь как список, а затем преобразовать его в словарь.Например, используя 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'})
0 голосов
/ 02 октября 2011

Вы можете попробовать:

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

Я не проверял это на своем телефоне прямо сейчас.

Редактировать: Кстати, я согласен с @wim,Я думаю, что использование каждого kv в качестве аргумента будет лучше для пользователя.

...