set_defaults
loops through the actions of the parser, and sets each default
attribute:
def set_defaults(self, **kwargs):
...
for action in self._actions:
if action.dest in kwargs:
action.default = kwargs[action.dest]
Your -n
argument (an action
object) was created when you defined the base_parser
. When each subparser is created using parents
, that action is added to the ._actions
list of each subparser. It doesn't define new actions; it just copies pointers.
So when you use set_defaults
on subparser2
, you modify the default
for this shared action.
This Action is probably the 2nd item in the subparser1._action
list (h
is the first).
subparser1._actions[1].dest # 'n'
subparser1._actions[1] is subparser2._actions[1] # true
If that 2nd statement is True
, that means the same action
is in both lists.
If you had defined -n
individually for each subparser, you would not see this. They would have different action objects.
I'm working from my knowledge of the code, not anything in the documentation. It was pointed out recently in Cause Python's argparse to execute action for default that the documentation says nothing about add_argument
returning an Action
object. Those objects are an important part of the code organization, but they don't get much attention in the documentation.
Copying parent actions by reference also creates problems if the 'resolve' conflict handler is used, and the parent needs to be reused. This issue was raised in
argparse conflict resolver for options in subcommands turns keyword argument into positional argument
and Python bug issue:
http://bugs.python.org/issue22401
A possible solution, for both this issue and that, is to (optionally) make a copy of the action, rather than share the reference. That way the option_strings
and defaults
can be modified in the children without affecting the parent.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…