At one point or another you might come over functions with a lot of arguments. Sometimes it makes sense to combine some of the arguments into super-arguments. I've often done this with dicts, but now I'm looking at better ways of doing it.
I'd like to turn ...
def do_something(ax, ay, az, bu, bv, c):
# Do something
... into ...
def do_something(a, b, c):
# Do something
... where a
and b
contain their subvariations.
One way to do this is to do:
A = namedtuple('A', 'x, y, z')
a = A(ax, ay, az)
B = namedtuple('B', 'u, v')
b = B(bu, bv)
However, this seems simpler:
a = SimpleNamespace(x=ax, y=ay, z=az)
b = SimpleNamespace(u=bu, v=bv)
What is the drawback? The fact that a
and b
aren't well typed? They aren't A and B objects?
(Btw, don't worry about the variable names. I don't normally use as short variable names.)
-
2There is no drawbacks per se, they are just different things. For starter namedtuples are inmutable while namespaces are mutable. Is mutable better or worse than inmutable? It depends on what you need or want, in many cases it just doesn't matter. You function would probably work with any object with the required attributes, how to build it is up to the caller.Stop harming Monica– Stop harming Monica05/28/2017 14:10:48Commented May 28, 2017 at 14:10
-
1@Goyo Thank you. The "drawback" thing was a clumsy way of saying it. I didn't mean to imply one is inherently better than the other. Just wanted the pros and cons. Thanks, again.André Christoffer Andersen– André Christoffer Andersen05/28/2017 16:07:39Commented May 28, 2017 at 16:07
-
2shouldn't the 4th line look like "b = B(bu, bv)"?Alen Siljak– Alen Siljak12/22/2017 19:19:49Commented Dec 22, 2017 at 19:19
-
1@AlenSiljak Yes it should. I'll fix it now.André Christoffer Andersen– André Christoffer Andersen11/11/2018 08:27:58Commented Nov 11, 2018 at 8:27
-
Another detail to consider: IDE may not support one of them. Specifically, PyCharm in 2024.1 still does not support SimpleNamespace hints, but supports namedtuple.halt9k– halt9k09/21/2024 16:03:30Commented Sep 21, 2024 at 16:03
3 Answers 3
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.
Summary of SimpleNamespace
It allows 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.