This is explained pretty well in the types module description. It shows you that types.SimpleNamespace is roughly equivalent to this:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
This provides the following advantages over an empty class:
- It allows you to initialize attributes while constructing the object:
sn = SimpleNamespace(a=1, b=2) - It provides a readable
repr():eval(repr(sn)) == sn - It overrides the default comparison. Instead of comparing by
id(), it compares attribute values instead.
This is explained pretty well in the types module description. It shows you that types.SimpleNamespace is roughly equivalent to this:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
This provides the following advantages over an empty class:
- It allows you to initialize attributes while constructing the object:
sn = SimpleNamespace(a=1, b=2) - It provides a readable
repr():eval(repr(sn)) == sn - It overrides the default comparison. Instead of comparing by
id(), it compares attribute values instead.
A class types.SimpleNamespace provides a mechanism to instantiate an object that can hold attributes and nothing else. It is, in effect, an empty class with a fancier __init__() and a helpful __repr__():
>>> from types import SimpleNamespace
>>> sn = SimpleNamespace(x = 1, y = 2)
>>> sn
namespace(x=1, y=2)
>>> sn.z = 'foo'
>>> del(sn.x)
>>> sn
namespace(y=2, z='foo')
or
from types import SimpleNamespace
sn = SimpleNamespace(x = 1, y = 2)
print(sn)
sn.z = 'foo'
del(sn.x)
print(sn)
output:
namespace(x=1, y=2)
namespace(y=2, z='foo')
This answer may also be helpful.
Videos
SimpleNamespace is basically just a nice facade on top of a dictionary. It allows you to use properties instead of index keys. This is nice as it is super flexible and easy to manipulate.
The downside of that flexibility is that it doesn't provide any structure. There is nothing to stop someone from calling SimpleNamespace(x=ax, y=ay) (and del a.z at some point later). If this instance gets passed to your function, the exception occurs when you try to access the field.
In contrast, namedtuple lets you create a structured type. The type will have a name and it will know what fields it is supposed to have. You won't be able to make an instance without each of those field and they can't be removed later. Additionally, the instance is immutable, so you will know that the value in a.x will always be the same.
It's up to you to decide if you need the flexibility that SimpleNamespace gives you, or if you prefer to have the structure and guarantees provided by namedtuple.
I really like the answer about structured versus not, so I'm just providing a concrete example below.
SimpleNamespace will accept keys that begin with _. If you're looking for a quick and easy way to turn, say, JSON you don't control into objects with field names, this is very handy:
d = {"_id": 2342122, "text": "hi there!"} # Elasticsearch gives this id!
e = SimpleNamespace(**d) # works
Name = namedtuple("Name", sorted(d)) # ValueError so we can't do Name(**d)
Note above that you can see that namedtuple gives us a whole extra object that SimpleNamespace never will. Each SimpleNamespace is really a "unique snowflake", whereas namedtuple exists without ever being instantiated with any concrete values. Wherever you have need of abstractions that generalize on concrete values, you probably should prefer it.