Python argparse字典参数

34

我想从命令行接收一个dict(str -> str)参数。 argparse.ArgumentParser提供了吗?还是有其他的库可以使用?

针对命令行:

program.py --dict d --key key1 --value val1 --key key2 --value val2

我期望得到以下的字典:

d = {"key1": "val1", "key2": "val2"}

1
你是在进行参数解析吗?将其收集到字典中似乎并不是很有意义。 - ljk321
1
@skyline75489。是的。我解析了 d 参数。为什么列表可以作为参数,而字典不行.. - orion_tvv
1
参数可能非常复杂。它可以是可选或必需的,位置相关或非位置相关,需要值或不需要值。我在考虑添加dict支持会让事情变得更加复杂。 - ljk321
1
作为程序员,使用program.py --opt1 k1=v1,k2=v2,k3=v3不是更好吗? - shx2
1
另一个选项是将值作为(带引号的)JSON字符串输入。 - hpaulj
显示剩余3条评论
9个回答

47

如果您想要一起使用逗号分隔的字典键值对,请使用自定义操作的另一种解决方案--

import argparse
import sys
parser = argparse.ArgumentParser(description='parse key pairs into a dictionary')

class StoreDictKeyPair(argparse.Action):
     def __call__(self, parser, namespace, values, option_string=None):
         my_dict = {}
         for kv in values.split(","):
             k,v = kv.split("=")
             my_dict[k] = v
         setattr(namespace, self.dest, my_dict)

parser.add_argument("--key_pairs", dest="my_dict", action=StoreDictKeyPair, metavar="KEY1=VAL1,KEY2=VAL2...")

args = parser.parse_args(sys.argv[1:])
print args

运行中:

python parse_kv.py --key_pairs 1=2,a=bbb,c=4 --key_pairs test=7,foo=bar

输出:

Namespace(my_dict={'1': '2', 'a': 'bbb', 'c': '4', 'test': '7', 'foo': 'bar'})

如果你想在字符串中使用nargs而不是逗号分隔的值:

class StoreDictKeyPair(argparse.Action):
     def __init__(self, option_strings, dest, nargs=None, **kwargs):
         self._nargs = nargs
         super(StoreDictKeyPair, self).__init__(option_strings, dest, nargs=nargs, **kwargs)
     def __call__(self, parser, namespace, values, option_string=None):
         my_dict = {}
         print "values: {}".format(values)
         for kv in values:
             k,v = kv.split("=")
             my_dict[k] = v
         setattr(namespace, self.dest, my_dict)

parser.add_argument("--key_pairs", dest="my_dict", action=StoreDictKeyPair, nargs="+", metavar="KEY=VAL")

args = parser.parse_args(sys.argv[1:])
print args

跑步

python arg_test4.py --key_pairs 1=2 a=bbb c=4 test=7 foo=bar

输出:

values: ['1=2', 'a=bbb', 'c=4', 'test=7', 'foo=bar']
Namespace(my_dict={'1': '2', 'a': 'bbb', 'c': '4', 'test': '7', 'foo': 'bar'})

1
这绝对是最好的答案。 - mgalardini
被低估的回答。唉,我已经尽我所能地付出了所有爱。 - The Nate
2
非常好的答案(已经点赞),但是它不能将布尔字符串解析为布尔值,也不能将其他数据类型(如列表、字典)作为字符串传递。需要使用eval函数来计算值。提醒一下! - skyfail
1
我使用了 my_dict[k] = ast.literal_eval(v) 来实现类型转换。 - eqzx
__call__方法中,我不得不使用my_dict = getattr(namespace, self.dest, {})来使其在我切换到使用子解析器后正常工作,非常感谢,这太棒了。 - Daniel Griffin

23

我会使用类似这样的东西:

p = argparse.ArgumentParser()
p.add_argument("--keyvalue", action='append',
               type=lambda kv: kv.split("="), dest='keyvalues')

args = p.parse_args("--keyvalue foo=6 --keyvalue bar=baz".split())
d = dict(args.keyvalues)

你可以创建一个自定义操作,它会直接将解析的键值对附加到字典中,而不仅仅是累积(key, value)元组列表。(这正是skyline75489 所做的,我的答案不同之处在于使用一个带有自定义类型的单独的--keyvalue选项来指定键值对,而不是分别使用--key--value选项。)


谢谢。你的答案是最好的。 - orion_tvv
5
我建议使用kv.split("=", 1)来允许值中包含=,并确保最多只有两个元素。 - Ryan Fox
2
同时:如果args.keyvalues中有任何一个kv的长度小于2,则引发ValueError('值必须以"KEY=VALUE"的形式给出') - Ryan Fox

10

另一种简单的方法:

parser = argparse.ArgumentParser()
parser.add_argument('--key1')
parser.add_argument('--key2')
args = parser.parse_args()
my_dict = args.__dict__

4
不,我不想将参数转换为字典。这很容易做到:arguments = vars(parser.parse_args())。请结合示例重新阅读问题。 - orion_tvv
通常不建议访问您无法控制的类的 __dict__ 属性,因为其中可能包含意料之外的元数据。 - jtschoonhoven

5

使用 vars

d = vars(parser.parse_args())

感谢您的贡献。但是您的代码并没有解决问题。它只是将所有传递的参数转换为字典,但代码还应该能够处理未知的动态键。 - orion_tvv

3

Python以数组argv的形式接收参数。您可以使用它在程序内部创建字典。

import sys
my_dict = {}
for arg in sys.argv[1:]:
    key, val=arg.split(':')[0], arg.split(':')[1]
    my_dict[key]=val

print my_dict

命令行:

python program.py key1:val1 key2:val2 key3:val3

输出:

my_dict = {'key3': 'val3', 'key2': 'val2', 'key1': 'val1'}

注意:参数将以字符串形式提供,因此您需要将它们转换为存储数字值。
希望这能够帮助您。

我认为你的解决方案还可以,但有点不够优雅。
  1. 键和值不能包含“:”。
  2. 很难控制多个参数的顺序。
- orion_tvv
1
我所提供的不是唯一的解决方案,关键是你有一个参数列表 - ['key1','val1','key2','val2','key3','val3'],因此可以根据任何逻辑获取字典,例如:每个偶数元素(比如位置“i”)是键,其对应的值将在位置(i+1)上,只需使用步长为2的循环并存储字典即可。 - divyum

3

Python 一行代码 argparse 字典参数 argparse_dictionary.py

# $ python argparse_dictionary.py --arg_dict=1=11,2=22;3=33 --arg_dict=a=,b,c=cc,=dd,=ee=,
# Namespace(arg_dict={'1': '11', '2': '22', '3': '33', 'a': '', 'c': 'cc', '': 'dd'})

import argparse

arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
    '--arg_dict',
    action=type(
        '', (argparse.Action, ),
        dict(__call__=lambda self, parser, namespace, values, option_string: getattr(
            namespace, self.dest).update(
                dict([
                    v.split('=') for v in values.replace(';', ',').split(',')
                    if len(v.split('=')) == 2
                ])))),
    default={},
    metavar='KEY1=VAL1,KEY2=VAL2;KEY3=VAL3...',
)
print(arg_parser.parse_args())

3
好的,但是很抱歉 - 这是一段无法阅读的代码。请使用 pep8 规范。 - orion_tvv

0

解析输入的一种直接方法,例如:

program.py --dict d --key key1 --value val1 --key key2 --value val2

是:

parser=argparse.ArgumentParser()
parser.add_argument('--dict')
parser.add_argument('--key', action='append')
parser.add_argument('--value', action='append')
args = parser.parse_args()

如果我的内部解析器没错的话,这应该会产生以下结果

args = Namespace(dict='d', key=['key1','key2'], value=['value1','value2'])

你应该能够用以下代码从中构建一个字典:

adict = {k:v for k, v in zip(args.key, args.value)}

使用args.dict将其分配给具有该名称的变量需要一些非Python技巧。最好的方法是在另一个字典中创建一个具有此名称的元素。

another_dict = {args.dict: adict}

这个解决方案没有进行太多的错误检查。例如,它没有确保键和值的数量相同。它也不允许您创建多个字典(即重复的--dict参数)。它不需要任何特殊的顺序。--dict可以出现在--key key1对之后。几个--value参数可以放在一起。

chepner所做的将key=value绑定在一起可以解决其中许多问题。


0
如果您只是想将argparse输入转换为字典,Python 3.6中有一个简单的解决方案。例如:
import argparse 

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', help='the path to the input file')
parser.add_argument('-o', '--output', help='the path to the output file')
args = parser.parse_args()

arguments = dict(args._get_kwargs())

for k, v in arguments.items():
    print(k, v) 

给定命令行输入,例如python3 script_name.py --input 'input.txt' --output 'output.txt',代码将输出到终端:

input input.txt
output output.txt

2
不,我不想将参数转换为字典。可以通过隐藏字段轻松完成而无需使用黑客技巧:arguments = vars(parser.parse_args())。请重新阅读带有示例的问题。 - orion_tvv
arguments = vars(parser.parse_args()) 在这种情况下通常是您想要的。 - kontur

-1

至于当前的库,例如argparsedocoptclick,它们都不支持使用dict作为参数。我能想到的最好的解决方案是自定义一个argparse.Action来支持它:

import argparse

class MyAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        super(MyAction, self).__init__(option_strings, dest, nargs, **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        print '%r %r %r' % (namespace, values, option_string)
        value_dict = {}
        values.reverse()
        while len(values) > 0:
            v = eval(values.pop()).lstrip('--') # This is like crazy hack, I know.
            k = eval(values.pop())
            value_dict[k] = v

        setattr(namespace, self.dest, value_dict)

parser = argparse.ArgumentParser()
parser.add_argument('-d', action=MyAction, nargs='*')
args = parser.parse_args('-d "--key" "key1" "--value" "val1" "--key" "key2" "--value" "val2"'.split())

print(args)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接