python – 从命令行构造列表列表

我正在
python中编写一个小模拟,它应该根据命令行参数以不同的方式聚合结果.

运行模拟并且Simulation对象包含原始结果后,我想使用Simulation.sample(list_of_objects)方法或Simulation.sample_differently()方法为每个指定的采样器生成一些输出. list_of_objects应该是范围(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)

不幸的是,类型构造函数对每个单独的命令行参数进行操作,而不是对于为nargs≠None生成的几个命令行参数的列表,因此上述方法不起作用.

实现上述行为的最佳pythonic方法是什么?

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

Pythonic方式(或通常很好的编程)是将任务分解成碎片.使用argparse只解析输入,然后使用后续代码构造模拟对象的最终列表.

例如,我认为这个解析器会接受你所有的输入(我还没有测试过):

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的字符串列表和带有–sample-different的True / False值.

然后我可以从这些参数构建范围和模拟对象列表(再次未测试):

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将整个列表作为值进行处理,它可以处理并添加到命名空间.但与我概述的相比,它并没有保存任何编码.

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

这对定义可能会修复您的’–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模拟的原因:模拟…..我认为如果你把它重写为一个函数或类定义,那就不那么混乱了.太多的lambdas使代码变得模糊.

点赞