且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Python 中的参数依赖项 - 无法使其工作

更新时间:2022-05-23 22:37:23

这种参数间依赖解析后更容易实现.

This kind of inter argument dependency is easier to implement after parsing.

args = parser.parse_args()
if not namespace.git_user and namespace.clone:
    parser.error('"--clone" requires legal git user')

此时,git_userclone 都已经解析完毕,并且有了它们的最终值.

At that point, both git_user and clone have been parsed, and have their final values.

当您实现它时,自定义操作仅在存在 --gituser 参数时运行.所以我认为当你给它 --gituser 而没有 --clone 时它会引发错误.

As you implemented it, the custom action is run only when there's a --gituser argument. So I think it will raise the error when you give it --gituser without --clone.

您可以给 --clone 一个类似的自定义操作,但它也必须处理 store_true 详细信息.--clone --gituser value 序列会发生什么?clone 操作将在解析 gituser 值之前运行.像这样的测试会遇到一些棘手的参数顺序问题.

You could give --clone a similar custom action, but it would also have to handle the store_true details. And what should happen with the --clone --gituser value sequence? The clone action will be run before the gituser value has been parsed. Tests like this run into some tough argument order problems.

其他几个问题:

  • 您的自定义操作不存储任何值,无论错误如何.***自定义store 子类.

自定义操作应该引发 argparse.ArgumentError 而不是直接调用 parser.error.

custom actions should raise argparse.ArgumentError rather than call parser.error directly.

单元测试文件 test/test_argparse.py 有一个自定义操作的示例,其中包含这样的相互测试.但它只是一个玩具,验证这样的代码是允许的.

The unittest file, test/test_argparse.py has an example of custom actions with mutual tests like this. But it's just a toy, verifying that such code is allowed.

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

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

理论上,您可以实现一个 --clone 操作,该操作设置 --gituser 操作的 required 属性.这样,如果不使用 --gituserparse_args 的最终 required 动作测试将引发错误.但这需要保存对 out[95] 中显示的 Action 的引用(或在 parse._actions 列表中找到它.可行但混乱.

You could, in theory, implement a --clone action that sets the required attribute of the --gituser Action. That way, if --gituser is not used, the final required actions test of parse_args will raise an error. But that requires saving a reference to the Action displayed in out[95] (or finding that in the parse._actions list. Feasible but messy.

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

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

这是来自 test/test_argparse.py 的一对交互自定义操作类的示例.

Here's an example of a pair of interacting custom action classes from test/test_argparse.py.

class OptionalAction(argparse.Action):

    def __call__(self, parser, namespace, value, option_string=None):
        try:
            # check destination and option string
            assert self.dest == 'spam', 'dest: %s' % self.dest
            assert option_string == '-s', 'flag: %s' % option_string
            # when option is before argument, badger=2, and when
            # option is after argument, badger=<whatever was set>
            expected_ns = NS(spam=0.25)
            if value in [0.125, 0.625]:
                expected_ns.badger = 2
            elif value in [2.0]:
                expected_ns.badger = 84
            else:
                raise AssertionError('value: %s' % value)
            assert expected_ns == namespace, ('expected %s, got %s' %
                                              (expected_ns, namespace))
        except AssertionError:
            e = sys.exc_info()[1]
            raise ArgumentParserError('opt_action failed: %s' % e)
        setattr(namespace, 'spam', value)

NSargparse.Namespace 的简写.

class PositionalAction(argparse.Action):

    def __call__(self, parser, namespace, value, option_string=None):
        try:
            assert option_string is None, ('option_string: %s' %
                                           option_string)
            # check destination
            assert self.dest == 'badger', 'dest: %s' % self.dest
            # when argument is before option, spam=0.25, and when
            # option is after argument, spam=<whatever was set>
            expected_ns = NS(badger=2)
            if value in [42, 84]:
                expected_ns.spam = 0.25
            elif value in [1]:
                expected_ns.spam = 0.625
            elif value in [2]:
                expected_ns.spam = 0.125
            else:
                raise AssertionError('value: %s' % value)
            assert expected_ns == namespace, ('expected %s, got %s' %
                                              (expected_ns, namespace))
        except AssertionError:
            e = sys.exc_info()[1]
            raise ArgumentParserError('arg_action failed: %s' % e)
        setattr(namespace, 'badger', value)

它们用于

parser = argparse.ArgumentParser()
parser.add_argument('-s', dest='spam', action=OptionalAction,
        type=float, default=0.25)
parser.add_argument('badger', action=PositionalAction,
        type=int, nargs='?', default=2)

并且应该与:

'-s0.125' producing: NS(spam=0.125, badger=2)),
'42',                NS(spam=0.25, badger=42)),
'-s 0.625 1',        NS(spam=0.625, badger=1)),
'84 -s2',            NS(spam=2.0, badger=84)),

这是可以进行的交叉检查类型的示例.但我要重申的是,通常***在解析之后而不是在解析期间处理交互.

This is an example of the kind of cross checking that can be done. But I'll repeat that generally interactions are best handled after parsing, not during.

至于实现问题——如果用户没有给你--gituser,你的自定义操作永远不会被调用.optionalAction.__call__ 仅在使用该参数时使用.总是使用 positionals,但不使用 optionals.

As to the implementation question - if the user does not give you --gituser, your custom Action is never called. The Action.__call__ of an optional is only used when that argument is used. positionals are always used, but not optionals.