按字母顺序对 argparse 帮助进行排序

Posted

技术标签:

【中文标题】按字母顺序对 argparse 帮助进行排序【英文标题】:Sort argparse help alphabetically 【发布时间】:2012-08-29 09:57:58 【问题描述】:

我正在使用 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 或其他东西并手动解析 arg 列表......我会看看我是否能找到上面的文档...... 【参考方案1】:

您可以通过提供自定义HelpFormatter class 来做到这一点;其中的内部结构没有正式记录。这意味着在 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 是否曾经调用过 add_argument(单数)? @grieve:是的,它被我覆盖的HelpFormatter.add_arguments() 方法使用。 @MartijnPieters 嗯,这是有道理的。只是想知道未记录的 API 是否曾经绕过 add_arguments 支持 add_argument。 解释已经很清楚了。我应该接受它。我更喜欢在dest 上进行排序而不是在option_strings 上进行排序,但我想这完全是任意的……你经常搞乱 argparse 的内部结构吗?我花了很长时间在那里挖掘,想出一个不如你的解决方案......另外,你知道任何计划在 API 中公开这些功能吗?由于它是纯 python,我认为其他 python 实现以不同的方式做这件事不会是一个重大问题......【参考方案2】:

当您创建 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 开发人员不想让用户受制于此。 讽刺的是,argparse 的动机是扩展 optparse 代码库的丑陋和困难。【参考方案3】:

另一种选择,绝对比@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 上失败,则程序的执行并不重要什么的……

【讨论】:

这也是我考虑过的,而且似乎奏效了。但我不太愿意这样建议,因为我们无法轻易判断更改p._group_actions 的顺序会产生什么副作用。 p._actions 中的行动组呢?这些顺序是否可能以某种方式与这个顺序相关联并且也需要更改?所以没有对答案的支持,而是对挖掘代码和破解价值的心理+1 ;-) @LukasGraf -- 我不会保证这个解决方案不会搞砸,但我认为它很安全。一切似乎都是使用无序的字典解析出来的(在我看来,顺序只是为了格式化帮助消息而保留)。我尝试排序p._actions,但这似乎没有任何效果... 如果您只想更改帮助行的顺序,那么对_group_actions 列表进行排序是合乎逻辑的。【参考方案4】:

这类似于@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 的公开文档接口,但确实在定义解析器的过程中增加了一层。 (根据您的理念,将有关选项的信息与解析器的实现分开可能是一件好事。)

【讨论】:

【参考方案5】:

帮助中的参数顺序由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 对象,然后向其添加“节”来创建的。在这里,它循环通过_action_groups,将每个放在自己的部分中,并使用add_arguments 方法添加其动作(参数)。格式化程序是临时的,仅用于创建字符串(通常是多行)。

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

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

排序_actions会影响usage(无论是帮助的一部分还是独立的):

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

请注意,action_groups 不会传递给使用部分。使用部分会重新排序其操作,首先显示optionals,然后显示positionals

如果您想控制位置的解析顺序以及它们在使用中的顺序,请在 add_argument 阶段之前/期间对参数进行排序。

如果您只想控制帮助组中的顺序,请随时在._group_actions 列表中重新排序,无论是在调用格式化程序之前还是在其中。

usage 中还有其他关于控制操作顺序的 SO 问题。例如,有些人不希望在optionals 之后排序positionals

我同意 Formatter 类很麻烦。但在大多数情况下,它与 Parser 类是分开的。所以它可以被重写而对解析的影响最小。现有的 Formatter 子类只是调整低级方法,即控制换行和帮助行格式化的方法。解析器和格式化程序之间的重要接口是format_usageformat_help 方法,它们相对简单和高级。

子类化

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

【讨论】:

以上是关于按字母顺序对 argparse 帮助进行排序的主要内容,如果未能解决你的问题,请参考以下文章

按字母顺序对表格中的列名进行排序

如何在 swift 中按字母顺序对 JSON 字符串进行排序?

首先按字母顺序对对象数组进行排序,然后按数字排序

在mysql中按字母顺序对全名进行排序

按字母顺序对 c 字符串数组进行排序

首先按频率对字符串中的字符进行排序,然后按字母顺序排序