How to handle variable number of arguments (nargs='*')
Categories:
Mastering Variable Arguments with argparse: The 'nargs='*' Approach

Learn how to effectively handle an arbitrary number of command-line arguments in Python using argparse
's nargs='*'
feature, making your scripts flexible and robust.
When developing command-line tools in Python, you often encounter scenarios where your script needs to accept a variable number of arguments. For instance, a file processing utility might take one or more filenames, or a data analysis script could accept an arbitrary list of values. The argparse
module, Python's recommended command-line parsing library, provides a powerful mechanism to handle such cases: the nargs='*'
option.
Understanding nargs='*'
The nargs
argument in parser.add_argument()
specifies how many command-line arguments should be consumed. When you set nargs='*'
, it instructs argparse
to collect zero or more arguments into a list. This is incredibly useful for arguments that are optional and can appear multiple times, such as a list of files, names, or numerical inputs.
flowchart TD A[Script Execution] --> B{Parse Arguments} B --> C{Argument 'files' defined with nargs='*'} C --> D{User provides 0 files?} D -- Yes --> E[files = [] (empty list)] D -- No --> F{User provides 1+ files?} F -- Yes --> G[files = [file1, file2, ...] (list of strings)] G --> H[Process 'files' list] E --> H
Flowchart illustrating how nargs='*'
handles zero or more arguments.
When nargs='*'
is used, the parsed argument will always be a list, even if no arguments are provided (in which case it will be an empty list) or if only one argument is provided (it will be a list containing that single argument). This consistency simplifies your code, as you can always iterate over the argument's value.
import argparse
parser = argparse.ArgumentParser(description='Process a variable number of items.')
parser.add_argument(
'items',
metavar='ITEM',
type=str,
nargs='*',
help='An item to be processed (can be multiple)'
)
args = parser.parse_args()
if args.items:
print(f"Processing {len(args.items)} items: {args.items}")
for item in args.items:
print(f" - {item}")
else:
print("No items provided for processing.")
Basic example of using nargs='*'
to accept multiple items.
nargs='*'
will result in a list. Even if the user provides a single argument, args.items
will be ['single_item']
, not 'single_item'
.Combining with Other Arguments
You can combine nargs='*'
with other types of arguments, including required positional arguments and optional arguments. However, there are some important considerations regarding positional arguments.
Typically, nargs='*'
is best used for the last positional argument in your parser definition. If you place a positional argument with nargs='*'
before other positional arguments, argparse
might become ambiguous about which arguments belong to the nargs='*'
list and which belong to the subsequent positional arguments. While argparse
has rules to handle this, it can lead to unexpected parsing behavior and is generally discouraged for clarity.
import argparse
parser = argparse.ArgumentParser(description='Process files with an output directory.')
parser.add_argument(
'--output', '-o',
type=str,
default='.',
help='Specify an output directory'
)
parser.add_argument(
'files',
metavar='FILE',
type=str,
nargs='*',
help='List of files to process'
)
args = parser.parse_args()
print(f"Output directory: {args.output}")
if args.files:
print(f"Files to process: {args.files}")
else:
print("No files specified.")
Example combining nargs='*'
with an optional argument.
nargs='*'
positional argument before other required positional arguments. This can lead to parsing ambiguities. For example, parser.add_argument('first', nargs='*'); parser.add_argument('second')
will likely not behave as expected if first
consumes all available arguments.Practical Use Cases and Best Practices
The nargs='*'
option shines in various practical scenarios:
1. File Processing Utilities
Accepting one or more filenames for operations like copying, deleting, or analyzing. Users can specify script.py file1.txt file2.txt
or just script.py
to operate on a default set.
2. Data Input Lists
Providing a list of values (e.g., numbers, IDs) to a script. For example, script.py --data 10 20 30
.
3. Flexible Command Structure
When your script acts as a wrapper for another tool and needs to pass through an arbitrary number of arguments to that underlying tool.
When using nargs='*'
, always check if the resulting list is empty before attempting to process its contents. This prevents errors if the user doesn't provide any arguments for that specific option.
import argparse
parser = argparse.ArgumentParser(description='A script that can take multiple inputs.')
parser.add_argument(
'--names',
type=str,
nargs='*',
default=['Guest'], # Provide a default if no names are given
help='List of names to greet'
)
args = parser.parse_args()
print("Greeting people:")
for name in args.names:
print(f"Hello, {name}!")
Using nargs='*'
with a default
value for a list.
In the example above, if no --names
are provided, args.names
will default to ['Guest']
, ensuring that the greeting loop always has something to process. This is a common pattern to provide sensible defaults for variable arguments.