112

To interactively test my python script, I would like to create a Namespace object, similar to what would be returned by argparse.parse_args(). The obvious way,

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.parse_args()
Namespace()
>>> parser.parse_args("-a")
usage: [-h]
: error: unrecognized arguments: - a

Process Python exited abnormally with code 2

may result in Python repl exiting (as above) on a silly error.

So, what is the easiest way to create a Python namespace with a given set of attributes?

E.g., I can create a dict on the fly (dict([("a",1),("b","c")])) but I cannot use it as a Namespace:

AttributeError: 'dict' object has no attribute 'a'

PS. The new exit_on_error option looks like a promising alternative to creating a Namespace object, but it is severely broken, apparently by design.

sds
  • 58,617
  • 29
  • 161
  • 278

6 Answers6

213

You can create a simple class:

class Namespace:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

and it'll work the exact same way as the argparse Namespace class when it comes to attributes:

>>> args = Namespace(a=1, b='c')
>>> args.a
1
>>> args.b
'c'

Alternatively, just import the class; it is available from the argparse module:

from argparse import Namespace

args = Namespace(a=1, b='c')

As of Python 3.3, there is also types.SimpleNamespace, which essentially does the same thing:

>>> from types import SimpleNamespace
>>> args = SimpleNamespace(a=1, b='c')
>>> args.a
1
>>> args.b
'c'

The two types are distinct; SimpleNamespace is primarily used for the sys.implementation attribute and the return value of time.get_clock_info().

Further comparisons:

  • Both classes support equality testing; for two instances of the same class, instance_a == instance_b is true if they have the same attributes with the same values.
  • Both classes have a helpful __repr__ to show what attributes they have.
  • Namespace() objects support containment testing; 'attrname' in instance is true if the namespace instance has an attribute namend attrname. SimpleNamespace does not.
  • Namespace() objects have an undocumented ._get_kwargs() method that returns a sorted list of (name, value) attributes for that instance. You can get the same for either class using sorted(vars(instance).items()).
  • While SimpleNamespace() is implemented in C and Namespace() is implemented in Python, attribute access is no faster because both use the same __dict__ storage for the attributes. Equality testing and producing the representation are a little faster for SimpleNamespace() instances.
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 4
    The former simple class is actually types.SimpleNamespace: see https://docs.python.org/dev/library/types.html#types.SimpleNamespace. – Ofer Jul 11 '17 at 15:00
  • @Ofer: no, `SimpleNamespace` is not actually used by `argparse`; `argparse.Namespace` is a pure-Python class unique to that library, `types.SimpleNamespace` is the class originally developed for `sys.implementation` and later used for `time.get_clock_info()` as well. It's implemented in C. – Martijn Pieters Oct 03 '18 at 11:05
  • To implement `'attrname' in instance` as `argparse.Namespace` does, the `__contains__` method can be added to the class definition above: `def __contains__(self, item): return item in self.__dict__` – alleen1 Jun 23 '21 at 06:34
26

It is now recommended to use SimpleNamespace from the types module. It does the same thing as the accepted answer except for it will be faster and have a few more builtins such as equals and repr.

from types import SimpleNamespace

sn = SimpleNamespace()
sn.a = 'test'
sn.a

# output
'test'
Mitchell Walls
  • 1,041
  • 1
  • 8
  • 13
  • 5
    `argparse.Namespace` also implements `__repr__` and `__eq__`. `SimpleNamespace` *is* implemented in C, but only the equality testing and `repr()` output will be a bit faster because of that. Attribute access is just as fast because both use the exact same mechanism to store and look up attributes. `argparse.Namespace()` also implements `__contains__`, so you can use `if something in ns_instance`, and there is an undocumented `._get_kwargs()` method; `SimpleNamespace()` doesn't have either. – Martijn Pieters Oct 03 '18 at 11:08
4

First create a dict and then use the dict to create an namespace:

from argparse import Namespace
x = {'a': 1, 'b': 2}
ns = Namespace(**x)
print(ns.a) #output 1
Youjun Hu
  • 991
  • 6
  • 18
0

If you prefer short code, you only need to add one extra line to define an empty class:

class dummy:pass
obj = dummy()

where dummy can be any string not defined in your program. Then obj will be a Namespace.

You can also initialize some namespace members in one line:

class dummy:a=1;b=2
obj = dummy()

print(obj.a+' '+obj.b)

It is a design deficiency of Python that there is no straight-forward way of creating the most basic Python object (i.e., Namespace object) just like creating tuple, list, dict, etc. If I am the designer, I will let a=<> to create an empty Namespace a.

xuancong84
  • 1,412
  • 16
  • 17
0

Edit: as @sds said, this feature is currently broken in cpython

An alternative solution for your problem without creating a namespace instance could be the exit_on_error parameter of the ArgumentParser constructor.

If set to false, it should not exit your REPL.

Source: https://docs.python.org/3/library/argparse.html#argumentparser-objects

mame98
  • 1,271
  • 12
  • 26
-5

argparse documentation show various examples of what you're trying to do:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-a")
parser.parse_args(['-a 12'])
>>> Namespace(a=' 12')
El Bert
  • 2,958
  • 1
  • 28
  • 36
  • 2
    no good, as I explain in the question: a small error and the repl is _dead_ – sds Feb 05 '15 at 14:01
  • 1
    This doesn't let you interactively test your code that is using the `Namespace` instance. – Martijn Pieters Feb 05 '15 at 14:01
  • @sds I may be missing something on the 'repl is dead' part of your question but isn't it because the `ArgumentParser` you're using is missing the `add_argument` call ? I do understand that you don't want to recreate the same `ArgumentParser` in your test but it could be created in a separate method called in the test to be retrieved. @MartijnPieters can't you just do create the `ArgumentParser` this way and then pass it in you method that's using it ? `my_method_using_arg_parse(fake_namespace) – El Bert Feb 05 '15 at 14:10