按字母顺序排序argparse帮助

26
我将使用Python的(2.7) argparse功能,并希望通过选项自动按字母顺序排列生成的帮助信息。
默认情况下,帮助条目按它们添加的顺序排序*,如下所示:
p = argparse.ArgumentParser(description='Load duration curves and other plots')
p.add_argument('--first', '-f', type=int, default=1, help='First Hour')
p.add_argument('--dur', '-d', type=int, default=-1, help='Duration in Hours. Use -1 for all')
p.add_argument('--title', '-t', help='Plot Title (for all plots), default=file name')
p.add_argument('--interp', '-i', action="store_true", default=True, 
                help='Use linear interpolation for smoother curves')
...
args = p.parse_args()

当运行 python script -h 时,会产生以下输出:

usage: script.py [-h] [--first FIRST] [--dur DUR] [--title TITLE] [--interp]

Load duration curves and other plots

optional arguments:
  -h, --help            show this help message and exit
  --first FIRST, -f FIRST
                        First Hour
  --dur DUR, -d DUR     Duration in Hours. Use -1 for all
  --title TITLE, -t TITLE
                        Plot Title (for all plots), default=file name
  --interp, -i          Use linear interpolation for smoother curves

是否可以自动按字母顺序排序呢?这将是dur、first、h、interp、title。

*显然,解决方法是通过手动添加条目,使用p.add_argument按字母顺序添加,但我想避免这样做。


我认为你可以钩取p.show_help或其他东西,然后手动解析参数列表...我会看看能否找到相关文档... - Joran Beasley
5个回答

27
您可以通过提供自定义的 HelpFormatter 来实现此功能;但是其内部文档未公开。这意味着在不同 Python 版本之间可能存在兼容性问题,但我发现接口非常稳定:
from argparse import HelpFormatter
from operator import attrgetter

class SortingHelpFormatter(HelpFormatter):
    def add_arguments(self, actions):
        actions = sorted(actions, key=attrgetter('option_strings'))
        super(SortingHelpFormatter, self).add_arguments(actions)


p = argparse.ArgumentParser(...
    formatter_class=SortingHelpFormatter,
)

这里我按选项字符串(('--dur', '-d')等)进行排序,但您可以选择要按什么进行排序。这个简单的排序选项将单破折号选项放在最后,例如-h选项。

输出如下:

usage: [-h] [--first FIRST] [--dur DUR] [--title TITLE] [--interp]

Load duration curves and other plots

optional arguments:
  --dur DUR, -d DUR     Duration in Hours. Use -1 for all
  --first FIRST, -f FIRST
                        First Hour
  --interp, -i          Use linear interpolation for smoother curves
  --title TITLE, -t TITLE
                        Plot Title (for all plots), default=file name
  -h, --help            show this help message and exit

super(HelpFormatter 应该改为 super(SortingHelpFormatter - jterrace
add_argument(单数)的调用是否曾被使用过? - grieve
@grieve:是的,它被我重写的HelpFormatter.add_arguments()方法所使用。 - Martijn Pieters
为什么help总是最后一个?在我的解决方案中并不是这样,我本来以为它们大致相等... - mgilson
1
解释很清楚。我应该能够理解它。我更喜欢按dest排序而不是按option_strings排序,但我想这完全是任意的...你经常深入研究argparse的内部吗?我花了很长时间在那里挖掘,才想出一个不像你的解决方案那么好的解决方法...另外,您是否知道有计划将这些功能公开在API中?由于它是纯Python,我认为其他Python实现以不同的方式执行不会是一个重大问题... - mgilson
显示剩余3条评论

1
帮助中参数的顺序由parser.format_help方法决定:
Definition:  parser.format_help(self)
Source:
    def format_help(self):
        formatter = self._get_formatter()
        ...
        # positionals, optionals and user-defined groups
        for action_group in self._action_groups:
            formatter.start_section(action_group.title)
            formatter.add_text(action_group.description)
            formatter.add_arguments(action_group._group_actions)
            formatter.end_section()

help 是通过获取一个 formatter 对象来创建的,然后向其中添加“sections”。这里循环遍历了 _action_groups,将每个分组放入自己的 section 中,并使用 add_arguments 方法添加其 actions(参数)。该 formatter 是临时的,仅用于创建字符串(通常是多行)。

Action groups 包括默认的 postionalsoptionals,以及用户创建的任何分组。这些分组仅用于帮助,不用于解析。因此,action_group._group_actions 列表可以重新排序而不影响解析(解析器有自己的 action 列表,parser._actions)。

这证实了 @mgilson 的观察结果,即对 p._actions 进行排序不会影响帮助,但对 _group_actions 进行排序会影响。

_actions 进行排序将影响使用情况(无论是作为帮助还是独立使用):

    # usage
    formatter.add_usage(self.usage, self._actions,
                        self._mutually_exclusive_groups)

请注意,action_groups不会传递到使用部分。使用部分将重新排序其操作,先显示optionals,然后是positionals
如果您想要控制位置参数的解析顺序以及它们在使用中的顺序,请在add_argument阶段之前/期间对参数进行排序。
如果您只想控制帮助组中的顺序,请随意在._group_actions列表中重新排序,可以在调用格式化程序之前或其中进行排序。
已经有其他的SO问题涉及控制usage中操作的顺序。例如,有些人不希望在optionals之后按顺序排列positionals
我同意Formatter类很笨重。但就大部分而言,它与Parser类是独立的。因此,它可以进行重写,对解析几乎没有影响。现有的Formatter子类只是微调低级方法,这些方法控制行换行和帮助行格式。解析器和格式化程序之间的重要接口是format_usageformat_help方法,它们相对较简单且高级。

子类化

尽管 @grieve 引用了警告,但人们确实会继承 HelpFormatter 以适应他们自己的需求。唯一阻止人们这样做的是某种公司政策。所有警告告诉我们的是 argparse 开发人员没有试图想象或记录用户可能想要进行的所有更改。在过去的几年中,我甚至无法列举出我提出的建议。

1

当您创建ArgumentParser类时,可以传递帮助格式化程序: http://docs.python.org/library/argparse.html#formatter-class

因此,显然您可以使用提供的其中一个格式化程序,但是如果没有进行一些反向工程,就无法覆盖和替换它们:

>>> h = argparse.ArgumentDefaultsHelpFormatter
>>> print h.__doc__
Help message formatter which adds default values to argument help.

    Only the name of this class is considered a public API. All the methods
    provided by the class are considered an implementation detail.

我经常想到更多的内容应该向用户公开 :-/(除了提供一些格式化工具并基本上不允许用户以任何方式干扰它们),我认为这是因为代码相当丑陋和难以编写,argparse 的开发人员不想让用户接受这样的折磨。 - mgilson
1
这是具有讽刺意味的,因为 argparse 是由于 optparse 代码库的丑陋和难以扩展而激发起来的。 - chepner

1

这是一种替代方案,绝对比@MartijnPieters提出的方法更丑陋:

p = argparse.ArgumentParser()

#add arguements here

for g in p._action_groups:
    g._group_actions.sort(key=lambda x:x.dest)

将这段代码放在try/except子句中可能是个好主意,因为它只是格式化帮助,如果这段代码在AttributeError或其他错误上失败,对程序的执行并不会产生太大影响...


1
这是我也考虑过并且似乎可行的事情。但是我不太舒服建议这样做,因为我们无法轻易地确定更改p._group_actions顺序可能会产生什么副作用。那么p._actions中的操作组呢?它们的顺序是否与此相关,并且需要进行更改?所以对于这个答案没有点赞,但在挖掘代码和黑客价值方面有一个心理上的+1;-) - Lukas Graf
@LukasGraf -- 我不能保证这个解决方案不会搞砸事情,但我认为它是相当安全的。似乎所有东西都是使用无序字典进行解析的(对我来说,顺序只是为了格式化帮助消息而保留的)。我尝试对p._actions进行排序,但似乎没有任何效果... - mgilson
如果您只想更改帮助行的顺序,那么对_group_actions列表进行排序是很合乎逻辑的。 - hpaulj

1
这与@mgilson的答案类似。我以为我之前已经发布了这个,但显然没有。
d = dict()
d['--first'] = ('-f', "type=int", "default=1", "help='First Hour'")
d['--dur'] = ('-d', type=int, default=-1, help='Duration in Hours. Use -1 for all')
# etc

for prim_option in sorted(d):
    p.add_arguments(prim_option, *d[prim_option])

您可以调整字典中所使用的键,以及传递给sorted的参数和调用add_arguments的确切结构,以获得所需的排序顺序。这符合argparse的公开文档接口,但确实为定义解析器的过程添加了一层。 (根据您的哲学,将选项的信息与解析器的实现分离可能是一件好事。)


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