在 Python 中以前缀表示法处理命令行参数

Posted

技术标签:

【中文标题】在 Python 中以前缀表示法处理命令行参数【英文标题】:Processing command-line arguments in prefix notation in Python 【发布时间】:2011-02-19 08:52:33 【问题描述】:

我正在尝试在 Python 中解析一个命令行,如下所示:

$ ./command -o option1 arg1 -o option2 arg2 arg3

换句话说,该命令采用无限数量的参数,并且每个参数可以选择在前面加上一个-o 选项,该选项专门与该参数相关。我认为这被称为“前缀表示法”。

在 Bourne shell 中,我会执行以下操作:

while test -n "$1"
do
    if test "$1" = '-o'
    then
        option="$2"
        shift 2
    fi
    # Work with $1 (the argument) and $option (the option)
    # ...
    shift
done

看看 Bash 教程等,这似乎是公认的习惯用法,所以我猜 Bash 已经过优化,可以以这种方式处理命令行参数。

尝试在 Python 中实现这种模式,我的第一个猜测是使用pop(),因为这基本上是一个堆栈操作。但我猜这在 Python 上效果不佳,因为sys.argv 中的参数列表顺序错误,必须像队列一样处理(即从左侧弹出)。我读过列表没有针对在 Python 中用作队列进行优化。

所以,我的想法是:将argv 转换为collections.deque 并使用popleft(),使用reverse() 反转argv 并使用pop(),或者只使用int 列表索引本身。

有没有人知道更好的方法来做到这一点,否则我的哪个想法在 Python 中是最佳实践?

【问题讨论】:

【参考方案1】:

在 python 中,del sys.argv[1]bash 中的shift 执行相同的操作。在 python 中,sys.argv 是一个列表。删除索引1 上的元素与bash 中的shift 相同。

以下示例 python 脚本将有助于跳过参数中的 -o 并考虑所有其他参数。

#!/usr/bin/python
import sys

if __name__ == '__main__':
    while len(sys.argv) > 1:
        if sys.argv[1] != "-o":
            print sys.argv[1]
        del sys.argv[1]

【讨论】:

【参考方案2】:

可以通过导入 sys,然后将 sys.argv[1:] 分配给变量来获得 Bourne/Bash 中“shift”的 Python 功能等价物,例如“参数”。然后您可以使用 args = args[1:] 向左移动一次,或者使用更大的数字多次移动。参数索引将从 0 开始,而不是 1。上面的示例可能如下所示:

import sys
args = sys.argv[1:]
while len(args):
    if args[0] == '-o':
        option = args[1]
        args = args[2:] # shift 2
    # Work with args[0] (the argument) and option (the option)
    # ...
    args = args[1:] # shift

【讨论】:

【参考方案3】:

另一个标准库模块:argparse

p = argparse.ArgumentParser()
p.add_argument('-o', action='append')
for i in range(1, 4): p.add_argument('arg%d' % i)
args = p.parse_args('-o option1 arg1 -o option2 arg2 arg3'.split())
print args
# -> Namespace(arg1='arg1', arg2='arg2', arg3='arg3', o=['option1', 'option2'])

【讨论】:

我希望我知道 argparse。 Optparse 很好,但如果你希望你的参数有多个值,你必须创建自己的类。为什么 stdlib 中有这么多选项解析器?【参考方案4】:

无需重新发明***:getopt 模块正是为此而设计的。如果这不符合您的需求,请尝试optparse 模块,它更灵活但更复杂。

【讨论】:

感谢您的回答。您能否举例说明如何使用 getopt 或 optparse 实现“前缀表示法”模式。我以前用过这两种方法,但我认为你只能解析“选项”,然后是“非选项”,而不是选项参数、选项参数……我需要的方式。 非选项参数不是作为列表返回的吗?来自 parser.parse_args() ?【参考方案5】:

类似:

for arg in sys.argv[1:]:
  # do something with arg

应该适合你。除非您期望有大量的参数,否则我会选择最简单的代码(不要太担心性能)。 argv[1:] 忽略第一个 argv 值,这将是脚本的名称。

【讨论】:

【参考方案6】:

你可以的

argv.pop(0)

提取第一个元素并返回它。不过,这可能效率低下。可能是。我不确定argv 是如何在幕后实现的。 (话说回来,如果效率很重要,你为什么要使用 Python?)

不过,更 Pythonic 的解决方案是在不弹出元素的情况下遍历列表。像这样:

o_flag = False
for a in argv:
    if a == '-o':
        o_flag = True
        continue
    # do whatever
    o_flag = False

另外,我认为optparse 模块值得一提;它是处理 Python 程序中的选项和参数的标准,尽管对于这个任务来说它可能是多余的,因为你已经有了几个完美的功能解决方案。

【讨论】:

你是对的;如果性能是最重要的,我可以很容易地在 C 中实现这种模式。我想我的问题更多是关于如何实现 argv 以及如何最好地将它作为堆栈访问(因为它必须是内部堆栈)。 我的猜测是标准的 C 数组,尽管正如我所说,我不确定。如果您好奇,您可以随时查看 Python 解释器的源代码并尝试追踪数组实现。但是在实际中使用,真的无所谓。

以上是关于在 Python 中以前缀表示法处理命令行参数的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中以 1 退出

python和shell获取命令行参数的区别

Python处理命令行参数

传递命令行参数示例代码 (C 和 Python)

Python3之命令行参数处理

如何在 Python 中处理命令行参数?