coding style: lightweight/simplest way to create instances supporting attribute assignment?

Question:

I often have to create a result object instance to return complex values from functions and wonder what’s a nice Pythonic approach.

I am aware of the closely related attribute-assignment-to-built-in-object, but it is asking why he needs to hack a subclass workaround to use ‘object’. I understand why already. I am asking instead if there is existing support in the standard library to avoid using ‘object’ and the subclass hack, either through an existing class or function.

What’s the lightest, most Pythonic way to instantiate an instance that supports attribute assignment?

I am OK to be pointed right back to subclassing ‘object’ answer. It’s really no big deal – just want to know if I had missed a cleaner approach supported by the standard library or builtins.

Sample of what I am trying to do:

try:
    returnval = object()
    returnval.foo = 1
    returnval.bar = 2

    print "nnSuccess with %s" % (returnval), vars(returnval), "has __slots__:", hasattr(returnval, "__slots__"), "has __dict__:", hasattr(returnval, "__dict__")

except Exception, e:
    print "nnFailure with %s:%s" % (returnval, e), "has __slots__:", hasattr(returnval, "__slots__"), "has __dict__:", hasattr(returnval, "__dict__")

This fails, as expected, with

Failure with <object object at 0x102c520a0>:'object' object has no attribute 'foo' has __slots__: False has __dict__: False

I am not surprised. ‘object’ is a bare stub, and does not allow attribute assignment because it has no __dict__.

Instead I have to declare a placeholder class. Is there a cleaner way?

try:
    class Dummy(object): pass

    returnval = Dummy()
    returnval.foo = 1
    returnval.bar = 2
    print "nnSuccess with %s" % (returnval), vars(returnval), "has __slots__:", hasattr(returnval, "__slots__"), "has __dict__:", hasattr(returnval, "__dict__")

except Exception, e:
    print "nnFailure with %s:%s" % (returnval, e), "has __slots__:", hasattr(returnval, "__slots__"), "has __dict__:", hasattr(returnval, "__dict__")

This gives:

Success with <__main__.Dummy object at 0x102d5f810> {'foo': 1, 'bar': 2} has __slots__: False has __dict__: True

Using the Dummy/MyClass approach avoids these problems, but it gives off a mild code smell to litter my modules with Dummy classes.

Things that would not work/are not satisfactory:

Dictionaries. I would avoid this if I used a dict instead, but I would lose the simple returnval.foo access.

AttrDict implementations perhaps? But those come in 3rd party packages, not in standard lib.

Mocks. Not what I want to be using here, because this is not testing code and I want an exception thrown if returnval.foo does not exist.

Module/class attribute assignment. Yes, I could assign attribute to an existing object in the namespace, like a class or a module declaration. But then that would essentially be assigning attributes to a singleton and successive function calls would clobber each other.

Asked By: JL Peyret

||

Answers:

A lightweight way is to use a function as the container

>>> foo = lambda: 0
>>> foo.bar = 'bar'
>>> foo.baz = 'baz'

If you are making a bunch of immutable objects with the same attributes collections.namedtuple is probably more appropriate

>>> foo = namedtuple("foo", "bar, baz")
>>> Foo = foo('bar', 'baz')
>>> foo.bar
'bar'
>>> foo.baz
'baz'
Answered By: John La Rooy

Either use namedtuple or stick with a dictionary – if you really want an attribute access – create your own class for it:

>>> Dummy = namedtuple('Dummy', ['foo', 'bar'])
>>> d = Dummy(1, 2)
>>> d
Dummy(foo=1, bar=2)
>>> d.baz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Dummy' object has no attribute 'baz'
Answered By: matino

this is one of the uses of types.SimpleNamespace:

SimpleNamespace may be useful as a replacement for class NS: pass. However, for a structured record type use namedtuple() instead.

from types import SimpleNamespace

res = SimpleNamespace(foo=1,bar=2)
print(f"{res=} {res.foo=} {(res.foo == 1)=}")
res.zoom = 3
print(f"{res=}")

output:

res=namespace(foo=1, bar=2) res.foo=1 (res.foo == 1)=True
res=namespace(foo=1, bar=2, zoom=3)
Answered By: JL Peyret
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.