从命令行构建列表的列表

4

我正在使用Python编写一个小型模拟程序,根据命令行参数以不同的方式汇总结果。

一旦模拟运行完成并且 Simulation 对象包含原始结果,我想使用 Simulation.sample(list_of_objects) 方法或 Simulation.sample_differently() 方法为每个指定的采样器生成一些输出。 list_of_objects 应该是一个 range(N) 或在命令行上显式指定的列表。

例如,我希望发生以下计算:

$ simulation --sample 5
[Simulation.sample(range(5))]
$ simulation --sample-objects 0 1 2 3 a
[Simulation.sample([0, 1, 2, 3, "a"])]
$ simulation --sample 4 --sample-objects 1 3 "b"
[Simulation.sample(range(4)), Simulation.sample([1, 3, "b"])]
$ simulation --sample-differently --sample-objects 1
[Simulation.sample_differently(), Simulation.sample([1])]
$ simulation --sample-objects 0 1 2 --sample-objects 3 4
[Simulation.sample([0, 1, 2]), Simulation.sample([3, 4])]

我会按照以下方式来做。
def parse_objects_to_sampler(object_strings):
    objects = []
    for entry in object_strings:
        try:
            objects.append(int(entry))
        except ValueError:
            objects.append(entry)
    return lambda simulation: simulation.sample(objects))

parser = argparse.ArgumentParser()
parser.add_argument(
    "--sample", action=append,
    type=lambda x: lambda simulation: simulation.sample(range(int(x))))
parser.add_argument(
    "--sample-differently", action="append_const", dest="sample",
    const=Simulation.sample_differently)
parser.add_argument(
    "--sample-objects", nargs="*", action="append", dest="sample",
    type=parse_objects_to_sampler)

for sampler in parser.parse().sample:
    sampler(Simulation)

很不幸,type构造函数作用于每个单独的命令行参数,而不是针对nargs≠None生成的多个参数列表。因此上述方法无法实现。

那么如何以最佳的Python方式实现上述行为呢?

2个回答

1
我认为比我更有经验的人可以谈论“最具Python风格”,但我的方法是接受一个CSV字符串,然后使用parse_objects_to_sampler函数拆分字符串并进行进一步的逻辑处理。所以:
def parse_objects_to_sampler(input_string):
object_string = input_string.split(",")

objects = []
for entry in object_strings:
    try:
        objects.append(int(entry))
    except ValueError:
        objects.append(entry)
return lambda simulation: simulation.sample(objects))

然后你会调用,例如:
simulation --sample-objects "0,1,2,3,a"

希望这能得到你想要的结果!

1

type 应该专注于测试输入并将其转换为基本输入。它接受一个字符串作为输入,并返回一些对象,或者如果字符串无效则引发错误。要处理 nargs* 列表的项目作为稍后解析的聚合体,您需要处理。

Pythonic 方式(或一般良好的编程)是将任务分解成若干部分。使用 argparse 只解析输入,然后使用后续代码构造最终的 simulation 对象列表。

例如,我认为此解析器将接受您的所有输入(我没有测试过):

parser = argparse.ArgumentParser()
parser.add_argument("--sample", type=int, action='append')
parser.add_argument("--sample-differently", action="store_true")
parser.add_argument("--sample-objects", nargs="*", action="append")
args = parser.parse_args()

重点是接受一个整数参数--sample,一个字符串列表参数--sample-objects和一个True/False值参数--sample-differently
然后我可以根据这些参数构建范围列表和simulation对象(同样未经测试)。
alist = []
if args.sample_differently:
    alist.append(Simulation.sample_differently())
for i in args.sample:
    # is a number
    alist.append(Simulation.sample(range(i)))
for i in args.sample_objects:
    # i will be a list of strings
    def foo(i):
        # conditionally convert strings to integers
        res = []
        for j in i:
            try:
                j = int(j)
            except ValueError:
                pass
        res.append(j)
        return res
    alist.append(Simulation.sample(foo(i))

如果我做得正确,alist 应该与您想要的列表匹配。
您可以创建一个自定义的 Action 类,使用 Simulation.sample 执行此类添加操作。Action 获取整个列表作为 values,它可以处理并添加到 namespace。但与我概述的内容相比,它不会节省任何编码。

===============

这一对定义可能会修复你的" --samples-objects "参数:

def intorstr(astr):
        # conditionally convert strings to integers
        try:
            astr = int(astr)
        except ValueError:
            pass
        return astr

class SamplesAction(argparse._AppendAction):
    # adapted from _AppendAction
    def __call__(self, parser, namespace, values, option_string=None):
         values = Simulation.sample(values)
         items = _copy.copy(_ensure_value(namespace, self.dest, []))
         items.append(values)
         setattr(namespace, self.dest, items)

parser.add_argument("--sample-objects", nargs="*", 
    action=SamplesAction, dest="sample", type=intorstr)

我忽略了你使用“lambda simulation: simulation....”的原因。我认为,如果你将其重写为函数或类定义,会更清晰易懂。太多的“lambda”会使代码变得混乱。

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