首页 > 解决方案 > 使用 argparse 解析“安装样式”选项

问题描述

在 python 中解析“安装样式”选项的最佳选择是什么?

我需要解析argument=value与特定参数关联的列表,如下例所示:

myprogram --database type=mysql,user=root,passwd=secret,database=mydb --mail type=imap,server=imap.my.domain,user=myself,passwd=othersecret,port=999 --whatever key=val,key=val2,etcetera

我的第一个猜测是指定一个参数,然后手动解析它(仅示例代码):

from argparse import ArgumentParser

parser = ArgumentParser(description='Example only')
parser.add_argument('-D', '--database', nargs=1)
parser.add_argument('-M', '--mail', nargs=1)
parser.add_argument('-W', '--whatever', nargs=1)

args = parser.parse_args('--database type=mysql,user=root,passwd=secret,database=mydb --mail type=imap,server=imap.my.domain,user=myself,passwd=othersecret,port=999 --whatever key=val,key=val2,etcetera'.split())

def parse(a):
    d = {}
    for x in a.split(','):
        try:
            k, v = x.split('=')
        except ValueError:
            k = x
            v = None
        d[k] = v
    
args.database = parse(args.database)
args.mail = parse(args.mail)
args.whatever = parse(args.whatever)

...但我想知道是否有更好的方法。

标签: pythonargparse

解决方案


事实证明,处理这个问题的正确方法是实现一个自定义argparse.Action.

在特定情况下,这是合理的工作:

!/bin/env python3

from argparse import ArgumentParser, Action


class KeypairAction(Action):
    def __init__(self, option_strings, dest, keys=None, **kwargs):
        super().__init__(option_strings, dest, **kwargs)
        if keys is None:
            raise ValueError('must define "keys" argument')
        if isinstance(keys, dict):
            self.keys = keys
        elif isinstance(keys, (list, tuple)):
            self.keys = {}
            for k in keys:
                if isinstance(k, tuple):
                    k, v = k
                else:
                    v = None
                self.keys[k] = v

    def __call__(self, parser, namespace, values, option_string=None):
        d = {}
        for x in values.split(','):
            try:
                k, v = x.split('=')
            except ValueError:
                k = x
                v = None
            d[k] = v
        setattr(namespace, self.dest, d)


ap = ArgumentParser(description="My nice program")
ap.add_argument('-D', '--database', action=KeypairAction, keys=('type', 'host', 'port', 'user', 'pass', 'db'))
ap.add_argument('-M', '--mail', action=KeypairAction, keys=('type', 'host', 'port', 'user', 'pass'))
ap.add_argument('-W', '--whatever', action=KeypairAction, keys=('key1', 'key2', 'key3', 'etcetera'))

args = ap.parse_args('--database type=mysql,user=root,pass=secret,db=mydb '
                         '--mail type=imap,host=imap.my.domain,user=myself,pass=othersecret,port=999 '
                         '--whatever key1=val,key2=val2,etcetera'.split())
print(args)

这可以简单地扩展以处理默认值。


推荐阅读