在 Python 中使用状态解析命令行

Posted

技术标签:

【中文标题】在 Python 中使用状态解析命令行【英文标题】:Parse command line with state in Python 【发布时间】:2020-11-01 23:37:33 【问题描述】:

一些程序命令行的状态适用于状态设置选项后面的参数(rm 和 touch 的“--”参数就是一个例子;ffmpeg 因有状态参数解析而臭名昭著)。例如:

readmulticsv --cols 1,2,4 file1.csv --date_format "%Y-%m-%d" file2.csv --cols 4,3,9 file3.csv file4.csv

在这里,它将从 file1.csv 和 file2.csv 中提取第 1、2 和 4 列,然后从 file3.csv 和 file4.csv 中按此顺序提取第 4、3 和 9 列。此外,它将开始以默认的“%m/%d/%Y”格式解释 file1.csv 中的日期(在 --cols 参数的第一列中),但切换到“%Y-%m-%d” " 用于剩余的文件。我想要的是一个列表列表,其中每个元素列表都有文件名和相关状态变量的值:

[["file1.csv", "1,2,4", "%m/%d/%Y"],
 ["file2.csv", "1,2,4", "%Y-%m-%d"],
...
]

如果您手动遍历 sys.argv,那么实现这一点很简单。

有没有办法用 argparse 做到这一点?我的程序将 argparse 用于许多其他选项及其很好的帮助功能,并且整个代码都是围绕其 Namespace 对象编写的。我可以使用 parse_known_args() 并将其余部分留给“walk”方法,但这不包括 --cols、--date_format 以及帮助和命名空间中的文件。我试过找出一个 Action(),但我不知道如何在那里进行。用于设置的文档对我来说不是很清楚,我不知道如何访问现有状态。

是否有替代的 arg 解析器可以完成所有操作(帮助、默认值、命名空间)?

我的应用程序是一个通过读取 CSV 交易文件来计算股票基础、收益和增长的程序,其中投资在几十年来具有不同文件格式和格式变化的经纪人之间转移。我可以为每种旧格式编写一个转换器,但我宁愿编写一个直接从源数据工作的程序。

谢谢,

--jh--

【问题讨论】:

这听起来像是一个一次性的项目,所以如果没有人提供您正在寻找的解决方案,请考虑将其加载到关系数据库并依靠它来规范您的数据。只是一个想法。祝你好运! 每个标记的选项(例如'--cols')都是独立处理的。 “追加”action 类可用于将来自不同“列”的输入收集到一个列表中。这些可以是字符串,也可以是列表,具体取决于nargs。如果要编写自己的Action 子类,请查看现有子类是如何定义的。例如,“附加”确实从命名空间中获取现有值。多年来,我和 SO 上的其他人建议使用 Action 子类。另外请记住,您已经向您的用户(或您自己 6 个月后)解释了这一点。 程序是一次性的,但有状态命令行的问题不是。很多程序都有它们,但是在搜索的日子里,我还没有看到解析一个的 Python 示例。追加操作不记录其他选项的状态。困难在于在附加文件时将最新的 --cols 和 --date_format 与每个文件相关联。解释起来很简单:最新的 --cols 和 --date_format 适用于遇到的每个文件。从左到右读取命令行,这些设置在更改之前一直有效。 argparse 解析模型将标记的参数视为独立且无序的。将它们视为一系列命令或状态与该设计背道而驰。 这是一个可以按任何顺序接收它们的功能,但这并不能使其成为基本设计标准,即不能或不应该按接收顺序处理它们。有许多使用有序 args 的 Unix 命令(我引用了 ffmpeg),我不认为你可以找到一种命令行方式来完成我的目标而无需有序 args 以一种对用户来说更容易的方式,因为文件是有序的(但我很高兴被证明是错误的)。 Argparse 作为通用参数解析器提供,因此它应该处理这个问题,但我很高兴尝试不同的包。不过,我还没有找到。 【参考方案1】:

与早期的optparsegetopt 相比,argparse 增加的一个重要功能是处理positionals 的能力。它使用类似re 的语法和模式匹配来将字符串(来自sys.argv 列表)分配给位置和可选(标记)参数。

基本的解析例程是交替解析positionalsoptional

与:

--cols 1,2,4 file1.csv --date_format "%Y-%m-%d" file2.csv --cols 4,3,9 file3.csv file4.csv

我可以想象定义一个

parser.add_argument('--cols', nargs='+', action='append')
parser.add_argument('--date_format', nargs='+', action='append')

导致

args.cols = [['1,2,4','file1.csv'], ['4,3,9', 'file3.csv', 'file4.csv']]
args.date_format = [["%Y-%m-%d", 'file2.csv']]

argparse 不保留有关colsdate 选项如何交错的信息。

我很想在位置中收集“文件”名称,但是没有办法在每个可选之间对连续的位置进行排序。

在最近的一个 SO 中,我建议使用列表预先填充 args,例如

 argparse.Namespace(cols=[[]], date_format=[["%m/%d/%Y"]])

并更改cols 操作以替换最后一个空列表。一个新的date_format 将同时更新colsdate_format 以开始一个新的“状态”。

Python using diferent options multiple times with argparse

在默认的Action 子类中,__call__ 将新值写入属性(使用setattr),覆盖默认值或之前写入的值。 append 子类,获取属性 (getattr),附加到它并写回它。默认类仅适用于它们自己的dest

Action 可以访问的唯一“状态”是namespace。但这可能就足够了,您可以设计自定义操作子类来获取和保存适当的属性。自定义操作甚至可以写入和读取add_argument 调用中未说明的属性。 (在文档中,set_defaults 用于为子解析器添加函数属性。)

另一种自定义方法是定义一个新的Namespace 类。默认的很简单,只有一种显示方式。在可能的情况下,argparse 使用 getattrhasattrsetattr 与命名空间进行交互,因此它对该类施加了最小的限制。

所以在type 函数、action 子类、namespace 类和formatter 之间有很大的空间可以自定义argparse。但是您确实需要研究argparse.py 代码。并认识到您几乎无法更改基本解析顺序。

在解析之前处理 sys.argv 是另一种工具,后处理 args 命名空间也是如此。

【讨论】:

谢谢,这很有帮助,我一直很感激,并认为您已经投入其中。我花了一些时间研究 argparse.py 代码和您的帖子。阻碍我的是我自己编写 Python 类的经验不足。这就是我在这里问的原因。我可以看到原则上可以编写一个动作 AppendState 来存储位置 args,同时记录其他 args 列表的状态,生成我提到的列表列表。大概只有几行。我认为它会对 argparse.py 起到很好的补充作用。我会试一试,但在接下来的几天内不能。

以上是关于在 Python 中使用状态解析命令行的主要内容,如果未能解决你的问题,请参考以下文章

Python笔记:命令行参数解析

python 命令行参数解析学习

如何在C++中解析命令行参数

Python中最好用的命令行解析工具:argparse

Python解析Linux命令行

Python语言命令行参数解析接收参数执行脚本的三种方法