380

If I want to use the results of argparse.ArgumentParser(), which is a Namespace object, with a method that expects a dictionary or mapping-like object (see collections.Mapping), what is the right way to do it?

C:\>python
Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> args.baz = 'yippee'
>>> args['baz']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Namespace' object has no attribute '__getitem__'
>>> dir(args)
['__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__eq__', '_
_format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__
', '__str__', '__subclasshook__', '__weakref__', '_get_args', '_get_kwargs', 'ba
r', 'baz', 'foo']

Is it proper to "reach into" an object and use its __dict__ property?

I would think the answer is no: __dict__ smells like a convention for implementation, but not for an interface, the way __getattribute__ or __setattr__ or __contains__ seem to be.

Jason S
  • 184,598
  • 164
  • 608
  • 970

4 Answers4

599

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.

Nick T
  • 25,754
  • 12
  • 83
  • 121
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • 1
    The docs say: "The returned dictionary should not be modified: the effects on the corresponding symbol table are undefined." Which *may* only refer to the behavior of `vars()` (which is either `locals()` or `globals()`), but I'm not really sure. –  Jun 01 '13 at 23:43
  • 4
    hmm, I guess I don't really understand the difference between using `vars()` and `__dict__` – Jason S Jun 01 '13 at 23:48
  • 31
    @delnan Someone had made an incorrect edit to the docs and made an over-broad admonition. The docs were subsequently corrected. See http://docs.python.org/2.7/library/functions.html#vars While there are some special cases that have read-only dictionaries (such as locals and class dictionary proxies), the rest of the cases are updateable. The *vars(obj)* call is synonymous with obj.__dict__. In the case of an *argparse* namespace, *vars(args)* gives direct access to an updateable dictionary. – Raymond Hettinger Jun 02 '13 at 00:04
  • 1
    Worth pointing out that going the `vars` route retains the order the arguments were defined by the parser unlike `dict(args._get_kwargs())` – jxramos Oct 09 '19 at 00:19
  • why doesn't the obvious way work e.g. `dict(args)`? Or at least why is this not the top answer/recommended? – Charlie Parker Dec 14 '21 at 18:27
  • 6
    @CharlieParker The *dict()* constructor builds new dictionaries from a list of tuples or from keyword arguments. In contrast, the *vars()* builtin accesses the \__dict__ attribute to retrieve existing dictionaries enclosed by various objects include an argparse namespace. – Raymond Hettinger Dec 16 '21 at 01:17
  • I'm doing this so I can use slick pattern matching from https://peps.python.org/pep-0636 with a namespace from argparse. thus you get `match vars(parser.parse_args()) ...`, which is pretty slick, but it seems like a clooge. I'd rather have a static-type-ee way of destructuring. Put thoughts here: https://gist.github.com/Groostav/0968a3c32f5d9a1a0eb121c9133653bf. Given that a Namespace type is a kind've simple type on top of a dict, I would've thought it would destructure more nicely. – Groostav Jun 21 '23 at 05:23
123

Straight from the horse's mouth:

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'}

— The Python Standard Library, 16.4.4.6. The Namespace object

Community
  • 1
  • 1
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 1
    Still 'foo' misses '--'. Any idea to that? – user2678074 Apr 04 '17 at 13:49
  • 1
    @user2678074 this is intentional to match shell flags. The dashes are extraneous inside python execution. – Erich Sep 11 '17 at 20:21
  • `vars(args)` gives me `TypeError: 'dict' object is not callable` – user5359531 Nov 27 '17 at 21:46
  • 8
    @user5359531 you probably overwrote the global `vars` with a variable. You can use `__builtins__.vars` to access it directly, or `del vars` to stop shadowing it. – Nick T Jan 16 '18 at 20:02
  • @NickT Minor correction: Python functions are statically scoped so if you shadow a global variable in a function, that identifier will always refer to the local variable within that function, even before it's assigned or after `del`. At the module scope `del` _will_ work to "un-shadow" builtins. – augurar Oct 20 '19 at 23:08
  • why doesn't the obvious way work e.g. `dict(args)`? Or at least why is this not the top answer/recommended? – Charlie Parker Dec 14 '21 at 18:27
2

Note that if you're trying to treat args as a dict to find out if it has an attribute, you can just do the following instead:

if hasattr(args, 'someProperty'):
    a.someProperty

more info on hasattr and similar answer to another question

Also, you can get the value if you prefer using the similar getattr method:

value = getattr(args, 'someProperty')
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
0

Is it proper to "reach into" an object and use its dict property?

In general, I would say "no". However Namespace has struck me as over-engineered, possibly from when classes couldn't inherit from built-in types.

On the other hand, Namespace does present a task-oriented approach to argparse, and I can't think of a situation that would call for grabbing the __dict__, but the limits of my imagination are not the same as yours.

msw
  • 42,753
  • 9
  • 87
  • 112
  • 13
    It is perfectly okay to access the \_\_dict\_\_ attribute. Introspection is fundamental to the language. The attribute was made public for a reason :-) – Raymond Hettinger Jun 01 '13 at 23:56
  • 10
    But everything in Python is "public". There are no distinctions (except the leading underscore convention) between the implementation variables used in an instance, and the public interface it presents. Especially in a dictionary-like object: the line between instance methods, and dictionary values which are functions, is a bit blurry. – Jason S Jun 02 '13 at 00:58
  • If you're passing the arguments as named parameters? `do_something(**args.__dict__)` – OrangeDog Oct 26 '18 at 11:59
  • why doesn't the obvious way work e.g. `dict(args)`? Or at least why is this not the top answer/recommended? – Charlie Parker Dec 14 '21 at 18:27