I want to understand why this code works. Why can you give type
, const
and default
a built-in function?
def printList(i):
print("Integer:", i)
return(i)
def getParser():
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("integers", type=printList, nargs="+")
parser.add_argument("--sum", dest="accumulate", const=sum, default=max, nargs="?")
return(parser)
args = getParser().parse_args(["2", "3", "9", "5"])
print(args.integers)
print(args.accumulate(args.integers))
Output:
>>> Integer: 2
>>> Integer: 3
>>> Integer: 9
>>> Integer: 5
>>> ['2', '3', '9', '5']
>>> 9
I want to understand why it is working.
Edit:
You misunderstood me.
For example "type" I would expect to see "type=int", because you want only to allow Integers. So you give "type" something. But in my example "type" gives something to the "printList" functions, so that it can print it. Or for example "default". I would expect to give some value, liken an int or str. I give something to "default". But in my example I give the build-in function "max". Why does "max" get a list? Why is that working? That is what I want to know.
parser.add_argument("--sum", dest="accumulate", const=sum, default=max, nargs="?")
is, assuming you don't supply an argument with '--sum'
, a little bit like:
args.accumulate = sum if '--sum' in arg_list else max
# ^ it also needs to be in the right place!
Per the docs for nargs='?'
:
One argument will be consumed from the command line if possible, and produced as a single item. If no command-line argument is present, the value from default will be produced. Note that for optional arguments, there is an additional case - the option string is present but not followed by a command-line argument. In this case the value from const will be produced.
Functions are first-class objects in Python, and can be passed around like any other object, so there's no reason they can't be used for const
and default
.
Note that if you actually did supply an argument with '--sum'
, args.accumulate
would be that value instead:
>>> args = getParser().parse_args(["1", "--sum", "this one"])
('Integer:', '1')
>>> args.accumulate
'this one'
Short answer:
argparse
expects type
to be a callable (with limited provision for a string).
argparse
allows const
and default
to be almost anything. It doesn't do anything with them except assign them to an attribute (or pass them to your type
callable).
Long answer:
From the internal documentation, for class Action
:
type -- A callable that accepts a single string argument, and returns the converted value. The standard Python types str, int, float, and complex are useful examples of such callables. If None, str is used.
default -- The value to be produced if the option is not specified.
const -- The value to be produced if the option is specified and the option uses an action that takes no values.
parse.add_argument
creates an Action
, using the action
parameter to specify the subclass. You can look at this object interactively or with:
a = parser.add_argument(....)
print(repr(a))
Parameters like type
, nargs
, default
are stored as attributes of this object.
At some depth in the parse_args
action, parser._get_values()
ends up calling parser._get_value
on each string that is allocated to a particular action.
def _get_value(self, action, arg_string):
type_func = self._registry_get('type', action.type, action.type)
if not callable(type_func):
msg = _('%r is not callable')
raise ArgumentError(action, msg % type_func)
# convert the value to the appropriate type
try:
result = type_func(arg_string)
This looks in a dictionary ( parser._registries
) to see if action.type
has been defined (as a string). If not, it assumes action.type
is itself a function. The type
is used in the result = type_func(arg_string)
line.
As a default None
is the only registered type
def identity(string):
return string
argparse.FileType
is a factory that creates a type
function, one that can open a file (ie take a file name and return an open file).
int
, float
are builtin Python functions that take a string and return a value, or raise an error if there is a problem. These specify the function that is called to convert the string. They do not, directly, specify that the string must represent an integer or float. Users sometimes mistakenly use boolean
in the same way.
The action
parameter is looked up in the _registries
. All of the default action classes have an entry, hence we use values like store
, store_true
, and append
. But we can also specify a custom Action class.
As for default
, early in the parse_args
stack, this line is executed for each action (defined argument):
setattr(namespace, action.dest, action.default)
This makes not assumptions about the nature of action.default
. It could be None
, a string, a number, a list, a function, or any Python object.
Near the end of parse_args
, it may pass action.default
through the type
function. It does this only if default
is a string.
setattr(namespace, action.dest, self._get_value(action, action.default))
Look at your args
. It probably looks like:
Namespace(integers=['2', '3', '9', '5'], accumulate=max)
Since you did not specify a --sum
option, the default value is placed in the namespace without change or checking. If you had specified --sum
, but without an argument, then accumulate=sum
(the const
parameter) would be in the name space. But --sum some_other_function
would have created accumulate="some_other_function"
.
Your chosen type
function has the side effect of printing its input, but it returns the string unchanged, hence you see strings in the integers
list.
When you execute
args.accumulate(args.integers)
It is the same as
max(["2", "3", "9", "5"])
which returns "9"
- the string, not the number.
sum(["2", "3", "9", "5"])
raise an error. The misnamed printList
only prints a string, not a list, and does not convert its values to integers.
Sorry to be long winded, but you did ask what is going on. In short, argparse
is written to give the user a lot of power, but it also tries to handle many common cases in a simple default manner.
Because functions are first-class objects in Python. They can be bound, accessed, and moved around just like any other type.
>>> foo = max
>>> foo(2, 3, 1)
3
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.