How to Treat Python Argparse.Namespace() as a Dictionary

What is the right way to treat Python argparse.Namespace() as a dictionary?

You can access the namespace's dictionary with vars():

>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> d = vars(args)
>>> d
{'foo': 1, 'bar': [1, 2, 3]}

You can modify the dictionary directly if you wish:

>>> d['baz'] = 'store me'
>>> args.baz
'store me'

Yes, it is okay to access the __dict__ attribute. It is a well-defined, tested, and guaranteed behavior.

Convert argparse Namespace to dict recursively

I don't think there's an already-made recursive solution, but here's a simple one:

def namespace_to_dict(namespace):
return {
k: namespace_to_dict(v) if isinstance(v, argparse.Namespace) else v
for k, v in vars(namespace).items()
}


>>> namespace_to_dict(args)
{'foo': 1, 'bar': [1, 2, 3], 'c': {'foo': 'a'}}

Python argparse dict arg

Here's another solution using a custom action, if you want to specify dict key pairs together comma-separated --

import argparse
import sys
parser = argparse.ArgumentParser(description='parse key pairs into a dictionary')

class StoreDictKeyPair(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
my_dict = {}
for kv in values.split(","):
k,v = kv.split("=")
my_dict[k] = v
setattr(namespace, self.dest, my_dict)

parser.add_argument("--key_pairs", dest="my_dict", action=StoreDictKeyPair, metavar="KEY1=VAL1,KEY2=VAL2...")

args = parser.parse_args(sys.argv[1:])
print args

Running:

python parse_kv.py --key_pairs 1=2,a=bbb,c=4 --key_pairs test=7,foo=bar

Output:

Namespace(my_dict={'1': '2', 'a': 'bbb', 'c': '4', 'test': '7', 'foo': 'bar'})

If you want to use nargs instead of comma-separated values in string:

class StoreDictKeyPair(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
self._nargs = nargs
super(StoreDictKeyPair, self).__init__(option_strings, dest, nargs=nargs, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
my_dict = {}
print "values: {}".format(values)
for kv in values:
k,v = kv.split("=")
my_dict[k] = v
setattr(namespace, self.dest, my_dict)

parser.add_argument("--key_pairs", dest="my_dict", action=StoreDictKeyPair, nargs="+", metavar="KEY=VAL")

args = parser.parse_args(sys.argv[1:])
print args

Running

python arg_test4.py --key_pairs 1=2 a=bbb c=4 test=7 foo=bar

Outputs:

values: ['1=2', 'a=bbb', 'c=4', 'test=7', 'foo=bar']
Namespace(my_dict={'1': '2', 'a': 'bbb', 'c': '4', 'test': '7', 'foo': 'bar'})

Update argparse namespace with other namespace/dictionary

In the subparser Action class, argparse.py uses:

    subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
for key, value in vars(subnamespace).items():
setattr(namespace, key, value)

to copy update namespace with values from subnamespace. argparse uses the generic setattr to set values, minimizing assumptions about valid names.

You might also be able to use

 namespace.__dict__.update(subnamespace.__dict__)

but I haven't tested it.

Unpacking arguments from argparse

https://docs.python.org/3/library/argparse.html#the-namespace-object

This class is deliberately simple, just an object subclass with a readable string representation. If you prefer to have dict-like view of the attributes, you can use the standard Python idiom, vars():

>>>
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> args = parser.parse_args(['--foo', 'BAR'])
>>> vars(args)
{'foo': 'BAR'}

Note that one of the big advances, or changes at least, from optparse to argparse is that positional arguments, such as yours, are treated the same as optionals. They both appear in the args Namespace object. In optparse, positionals are just the left overs from parsing defined options. You could get the same effect in argparse by omiting your arguments and using parse_known_args:

parser = argparse.ArgumentParser()
args, extras = parser.parse_known_args()

args is now a Namespace, and extras a list. You could then call your function as:

myfoo(*extras, **vars(args))

For example:

In [994]: import argparse
In [995]: def foo(*args, **kwargs):
.....: print(args)
.....: print(kwargs)
.....:
In [996]: parser=argparse.ArgumentParser()
In [997]: parser.add_argument('-f','--foo')
Out[997]: _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [998]: args,extras = parser.parse_known_args(['-f','foobar','arg1','arg2'])
In [999]: args
Out[999]: Namespace(foo='foobar')
In [1000]: extras
Out[1000]: ['arg1', 'arg2']
In [1001]: foo(*extras, **vars(args))
('arg1', 'arg2')
{'foo': 'foobar'}

That same argparse paragraph shows that you can define your own Namespace class. It wouldn't be hard to define one that behaves like a dictionary (for use as **args) and as namespace. All argparse requires is that it works with getattr and setattr.

In [1002]: getattr(args,'foo')
Out[1002]: 'foobar'
In [1004]: setattr(args,'bar','ugg')
In [1005]: args
Out[1005]: Namespace(bar='ugg', foo='foobar')

another standard Python feature lets me pass vars(args) as a tuple:

In [1013]: foo(*vars(args).items())
(('foo', 'foobar'), ('bar', 'ugg'))
{}

For a similar answer from last January: https://stackoverflow.com/a/34932478/901925

Neatly pass positional arguments as args and optional arguments as kwargs from argpase to a function

There I give ideas on how to separate out 'positionals' from 'optionals' after parsing.


Here's a custom namespace class that includes, in its API, a means of returning itself as a dictionary:

In [1014]: class MyNameSpace(argparse.Namespace):
......: def asdict(self):
......: return vars(self)
......:
In [1015]: args = parser.parse_args(['-f','foobar'], namespace=MyNameSpace())
In [1016]: args
Out[1016]: MyNameSpace(foo='foobar')
In [1017]: foo(**args.asdict())
()
{'foo': 'foobar'}

Another idea - use one of the multiple nargs (2,'*','+') for the positional argument. Then you have only one name to type when passing it to your function.

parser.add_argument('pos',nargs='+')
args = ...
args.pos # a list, possibly empty
foo(*args.pos, **vars(args))

How to store an 'args.foo' value to a 'bar' variable?

As others have said, removing the line referencing args.learning_rate will lead to others finding your code cryptic or confusing.

But something like this could be used in code golfing, so I will offer a 'cryptic' way of doing this under the assumption that you have several arguments that you do not want to have to reassign line-by-line.

You could acquire the dictionary of args using the vars() built-in function or __dict__ attribute.

Please reference What is the right way to treat Python argparse.Namespace() as a dictionary.

>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> d = vars(args)
>>> d
{'foo': 1, 'bar': [1, 2, 3]}

Afterwards you could convert these keys to variables and assign the values to the variable like so:

for k, v in d.items():
exec(f'{k} = {v}')
print(foo) # 1
print(bar) # [1, 2, 3]

Please see Using a string variable as a variable name for additional insight to why exec() may be bad practice and setattr may be more appropriate if you resort to this method.

Python Command Args

I'm not entirely sure what your goal is. But if that's literally all you have to do, you don't have to get very complicated:

import sys
print int(sys.argv[1]) + 2

Here is the same but with some nicer error checking:

import sys

if len(sys.argv) < 2:
print "Usage: %s <integer>" % sys.argv[0]
sys.exit(1)

try:
x = int(sys.argv[1])
except ValueError:
print "Usage: %s <integer>" % sys.argv[0]
sys.exit(1)

print x + 2

Sample usage:

C:\Users\user>python blah.py
Usage: blah.py <integer>

C:\Users\user>python blah.py ffx
Usage: blah.py <integer>

C:\Users\user>python blah.py 17
19

Python: load variables in a dict into namespace

Consider the Bunch alternative:

class Bunch(object):
def __init__(self, adict):
self.__dict__.update(adict)

so if you have a dictionary d and want to access (read) its values with the syntax x.foo instead of the clumsier d['foo'], just do

x = Bunch(d)

this works both inside and outside functions -- and it's enormously cleaner and safer than injecting d into globals()! Remember the last line from the Zen of Python...:

>>> import this
The Zen of Python, by Tim Peters
...
Namespaces are one honking great idea -- let's do more of those!

Use the current namespace with pars_args() in the argparse package in Python3

You may be confusing 2 uses of 'namespace'.

One namespace is the dictionary that holds the variables of this module or function.

parser.parse_args() creates an argparse.Namespace object, and puts its values in it, using setattr. This class definition is pretty simple.

parser.parse_args(namespace=myNamespace) should work with any object that accepts the setattr, getattr and hasattr methods. A dictionary does not work.

If given a dictionary the first error I get is in

setattr(namespace, action.dest, default)
AttributeError: 'dict' object has no attribute 'checked'

Are you asking this because you want to put the argparse attributes directly in the local namespace dictionary?

You can convert a Namespace to a dictionary with vars(args). And you can add those items to another dictionary with update (e.g. locals().update(vars(args)) ).

Or if you have a function that takes **kwargs, you could use: foo(**vars(args)) to put those arguments into the function's namespace. This does a better job of localizing the change. It also lets you define the variables with the normal keyword syntax.



Related Topics



Leave a reply



Submit