While you could use the usual suspects,
like a dictionary, a NamedTuple
, a dataclass
,
or even an “empty” class,
there is yet another way: SimpleNamespace
.
>>> from types import SimpleNamespace
>>> simple_ns = SimpleNamespace(a=1, b="two")
>>> simple_ns
namespace(a=1, b='two')
>>> simple_ns.a
1
>>> simple_ns.b
'two'
>>>
implementation
This builtin
is implemented in C,
but the Python docs show how it would look like in Python:
class SimpleNamespace:
def __init__(self, /, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
items = (f"{k}={v!r}" for k, v in self.__dict__.items())
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
That is the reason why we have this nice representation.
>>> simple_ns
namespace(a=1, b='two')
As a plus, you can even compare instances by attribute comparison,
and not only by id
!
>>> another_ns = SimpleNamespace(a=1, b="two")
>>> simple_ns == another_ns
True
>>> simple_ns is another_ns
False
why not a class?
>>> class NS:
... pass
...
>>> class_ns = NS()
>>> class_ns.a = 1
>>> class_ns.b = "two"
>>> class_ns
<__main__.class_ns object at 0x7f01cc670c18>
>>> another_class_ns = NS()
>>> another_class_ns.a = 1
>>> another_class_ns.b = "two"
>>> class_ns == another_class_ns
False
>>>
fun fact
When you have a look at the above implementation, it looks like only the dictionaries are compared - but this is not true:
>>> simple_ns == class_ns
False
The reason is simple. The above Python implementation is not complete.
The C implementation also performs a type check.
static PyObject *
namespace_richcompare(PyObject *self, PyObject *other, int op)
{
if (PyObject_IsInstance(self, (PyObject *)&_PyNamespace_Type) &&
PyObject_IsInstance(other, (PyObject *)&_PyNamespace_Type))
return PyObject_RichCompare(((_PyNamespaceObject *)self)->ns_dict,
((_PyNamespaceObject *)other)->ns_dict, op);
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
I created a bug report on the cpython issue tracker, let’s see what the core developers think about it. Update 2020-11-14 My pull request was merged.
why not just object?
>>> obj = object()
>>> obj
<object object at 0x7f01cddfe3c0>
>>> obj.a = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'a'
So, there are several reasons, but reason nr. 1 is, that you cannot add attributes.
thanks
Thanks Anthony Sottile for sharing his video easy fake objects with python’s SimpleNamespace