[英]Custom parsing function for any number of arguments in Python argparse
我有一个脚本,可通过命令行获取命名参数。 参数之一可以多次提供。 例如,我想运行一个脚本:
./script.py --add-net=user1:10.0.0.0/24 --add-net=user2:10.0.1.0/24 --add-net=user3:10.0.2.0/24
现在,我想执行一个argparse动作,该动作将解析每个参数并将结果存储在dict中,例如:
{ 'user1': '10.0.0.0/24',
'user2': '10.0.1.0/24',
'user3': '10.0.2.0/24' }
另外,如果没有提供任何值,则应该提供一个默认值。 喜欢
./script.py
应该具有以下命令:
{'user': '192.168.0.0/24'}
我相信我必须为argparse建立一个自定义动作。 我想出的是:
class ParseIPNets(argparse.Action):
"""docstring for ParseIPNets"""
def __init__(self, option_strings, dest, nargs=None, **kwargs):
super(ParseIPNets, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
for value in values:
location, subnet = values.split(':')
namespace.user_nets[location] = subnet
parser = argparse.ArgumentParser(description='foo')
parser.add_argument('--add-net',
nargs='*',
action=ParseIPNets,
dest='user_nets',
help='Nets subnets for users. Can be used multiple times',
default={"user1": "198.51.100.0/24"})
args = parser.parse_args()
当我需要使用默认值时,这很好用:
test.py
Namespace(user_nets={'user1': '198.51.100.0/24'})
但是,当我添加参数时-它们会附加到默认值。 我的期望是应将它们添加到一个空字典中:
test.py --add-net=a:10.0.0.0/24 --add-net=b:10.1.0.0/24
Namespace(user_nets={'a': '10.0.0.0/24', 'b': '10.1.0.0/24', 'user1': '198.51.100.0/24'})
什么是达到我需要的正确方法?
使用可变的默认参数(在您的情况下为dict)通常不是一个好主意,请参见此处获取说明:
每次调用该函数时,都要使用默认的arg来表示未提供任何参数,从而创建一个新对象(通常不选择它是一个不错的选择)。
很明显argparse
内部将默认值作为结果对象的初始值,因此您不应该在add_argument
调用中直接设置默认值,而add_argument
做一些额外的处理:
parser.add_argument('--add-net',
action=ParseIPNets,
dest='user_nets',
help='Nets subnets for users. Can be used multiple times',
default = {})
args = parser.parse_args()
if len(args.user_nets) == 0:
args.user_nets['user1'] = "198.51.100.0/24"
另外,如果您希望获得更好的用户体验,则可以利用Python处理可变默认参数的方式:
class ParseIPNets(argparse.Action):
"""docstring for ParseIPNets"""
def __init__(self, option_strings, dest, nargs=None, **kwargs):
super(ParseIPNets, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, values, option_string=None, first=[True]):
if first[0]:
namespace.user_nets.clear()
first[0] = False
location, subnet = values.split(':')
namespace.user_nets[location] = subnet
parser.add_argument('--add-net',
action=ParseIPNets,
dest='user_nets',
help='Nets subnets for users. Can be used multiple times',
default={"user1": "198.51.100.0/24"})
args = parser.parse_args()
这样,如果存在该选项,则将清除可选默认值。
但要注意 :这将在对剧本第一次调用才有效。 这是可以接受的,因为parser.parse_args()
在脚本中仅应调用一次。
Ancilliary句话:我取下nargs='*'
,因为我觉得它比这里有用的更危险,如果你调用它的方式,并且也去掉了错误的循环values
总是使用values
:
test.py --add-net=a:10.0.0.0/24 --add-net=b:10.1.0.0/24
nargs='*'
对于以下语法有意义:
test.py --add-net a:10.0.0.0/24 b:10.1.0.0/24
代码将是:
def __call__(self, parser, namespace, values, option_string=None, first=[True]):
if first[0]:
namespace.user_nets.clear()
first[0] = False
for value in values:
location, subnet = value.split(':')
namespace.user_nets[location] = subnet
解决此问题的第一种方法是使用action='append'
,然后在解析后将结果列表转换成字典。 代码量将是相似的。
默认情况下,“ append”确实存在相同的问题。 如果default=['defaultstring']
,则列表也将从该值开始。 我会使用默认的默认值([]参见下文)解决此问题,并在后期处理中添加默认值(如果列表仍然为空或无)。
关于默认值的注释。 在parse_args
,所有操作默认值都会添加到名称空间中(除非名称空间作为parse_args
的参数提供)。 然后解析命令行,每个动作对名称空间执行其自身的操作。 最后,所有剩余的字符串默认值都将通过type
函数进行转换。
在您的情况下, namespace.user_nets[location] = subnet
查找user_nets
属性,并添加新条目。 该属性默认情况下已初始化为字典,因此默认值出现在最终字典中。 实际上,如果您将默认值保留为None
或某些字符串,则您的代码将无法工作。
_AppendAction
类的call
可能是_AppendAction
:
def __call__(self, parser, namespace, values, option_string=None):
items = _copy.copy(_ensure_value(namespace, self.dest, []))
items.append(values)
setattr(namespace, self.dest, items)
_ensure_value
是在argparse
定义的函数。 _copy
是它导入的标准copy
模块。
_ensure_value
作用类似于字典get(key, value, default)
,除了namespace
对象外。 在这种情况下,如果self.dest
还None
值(或值为None
),它将返回一个空列表。 因此,它确保追加以列表开头。
_copy.copy
确保将值附加到副本中。 这样, parse_args
将不会修改default
。 它避免了@miles82
指出的问题。
因此,“追加操作”在call
本身中定义了初始的空列表。 并使用copy
来避免修改任何其他默认值。
您想要values
而不是value
吗?
location, subnet = values.split(':')
我倾向于将此转换放入类型函数中,例如
def dict_type(astring):
key, value = astring.split(':')
return {key:value}
这也是进行错误检查的好地方。
在操作中,或将其解析后,可以使用update
将它们添加到现有字典中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.