421

I want to create a dynamic object (inside another object) in Python and then add attributes to it.

I tried:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

but this didn't work.

Any ideas?

edit:

I am setting the attributes from a for loop which loops through a list of values, e.g.

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

In the above example I would get obj.a.attr1, obj.a.attr2, obj.a.attr3.

I used the setattr function because I didn't know how to do obj.a.NAME from a for loop.

How would I set the attribute based on the value of p in the example above?


For details on why it doesn't work, see Can't set attributes on instance of "object" class.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
John
  • 21,047
  • 43
  • 114
  • 155
  • 4
    What do you mean by "didn't work"? I assume it raised an AttributeError exception, right? – Josh Wright May 13 '10 at 14:39
  • 2
    yeah. 'object' object has no attribute 'somefield' – John May 13 '10 at 14:42
  • 9
    Why are you doing this? A generic "object" has no actual *meaning*. What is the *meaning* of the thing you are creating? Why is it not a proper class or namedtuple? – S.Lott May 13 '10 at 14:45
  • 2
    The example is not minimal and confusing for me or I just don't see why you don't work with some `a = object()` and you need `obj.a = object()`. Again I am talking about the example, in your actual code an object inside an object might be useful. – kon psych Jul 25 '17 at 02:51

19 Answers19

450

The built-in object can be instantiated but can't have any attributes set on it. (I wish it could, for this exact purpose.) It doesn't have a __dict__ to hold the attributes.

I generally just do this:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

When I can, I give the Object class a more meaningful name, depending on what kind of data I'm putting in it.

Some people do a different thing, where they use a sub-class of dict that allows attribute access to get at the keys. (d.key instead of d['key'])

Edit: For the addition to your question, using setattr is fine. You just can't use setattr on object() instances.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
FogleBird
  • 74,300
  • 25
  • 125
  • 131
  • 11
    it *can* be instantiated, just not used for anything useful once it has been done. `foo = object()` works, but you just can't do much of anything with it – Daniel DiPaolo May 13 '10 at 14:43
  • Hi. Thanks for the answer. I've updated my problem above. see the edit. do you know the answer to this? – John May 13 '10 at 14:55
  • sorry I still want to set it on the object. see update above. – John May 13 '10 at 15:06
  • I really like your answer and I think I will lean this direction in the future. I have used about everything else on this post except for this very simple, understandable, and readable methodology. Using ```type....``` or lambda was never my fav, like text vomit in my code. But this idea is great for using objects to hold properties. Leaves code more readable b/c when I see lambda I slow down my reading to 25% while your way makes total sense! Thanks. – Marc Oct 14 '16 at 00:31
  • 2
    great answer, the only thing I changed was to use `Struct` as the name of the class to make it more obvious. Saved me a ton of typing `["` and `"]`, Cheers! – pragman Oct 22 '16 at 14:16
  • Save a few characters by doing - `class Object: ...` – Yesh Jul 02 '20 at 07:10
  • A tiny detail, the `pass` statement is no longer necessary (at least with python 3.7). – Martin Riddar Jan 13 '22 at 08:25
260

You could use my ancient Bunch recipe, but if you don't want to make a "bunch class", a very simple one already exists in Python -- all functions can have arbitrary attributes (including lambda functions). So, the following works:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

Whether the loss of clarity compared to the venerable Bunch recipe is OK, is a style decision I will of course leave up to you.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • And in some cases, considering whether or not the `lambda` pattern fits for your use case may lead you to realize that something you'd originally thought of as data is actually more like a function anyway--or, in any case, a functor. – Kyle Strand Jul 30 '14 at 20:45
  • but... then obj.a is a *function*, not an object. This makes no sense, compared to FogleBird's answer. – naught101 Jan 26 '15 at 05:56
  • 5
    @naught101, a function **is** an object, in Python, so your objection is unfathomable. – Alex Martelli Jan 26 '15 at 06:02
  • @AlexMartelli: yes, obviously just about everything in python is an object. So why complicate things? it just adds a whole lot of unnecessary attributes and methods, and makes it uselessly callable... – naught101 Jan 26 '15 at 12:58
  • 8
    @naught101, avoiding the creation of a new type (reusing an existing one) does not complicate, it simplifies. Nowadays on might actually prefer `from argparse import Namespace` though I wish it lived elsewhere *e.g, `collection`) -- again reusing a now-existing type, just a better one, and still avoiding new-type creation. But, it wasn't there then:-). – Alex Martelli Jan 26 '15 at 14:58
  • 1
    I was looking for exactly this functionality, but `from argparse import Namespace` is so counter intuitive. Has it ever been suggested to move it, indeed, to `collections`? – schatten Jan 30 '15 at 08:22
  • @schatten, I don't know -- I haven't followed the python-ideas mailing list in a while -- I'm still mostly using Python 2.7 and my own ancient `Bunch` recipe anyway, and any changes in 3.5 or later would not benefit me for quite a while:-). BTW, I believe the Python standard library has other (essentially) implementations of Bunch, too -- `argparse.Namespace` is just one of them. – Alex Martelli Jan 30 '15 at 15:50
  • 1
    See answer below from "J F Sebastian" regarding SimpleNamespace from the types module. If your version of python supports it, this is the best solution (and exactly what SimpleNamespace is designed for) – Tim Richardson Feb 04 '16 at 23:40
  • @AlexMartelli could have even gone deeper there, 23 itself can be expressed as a bunch of functions. see "church encoding" :) – Trevor Merrifield Jun 25 '17 at 02:47
  • It became a pip package at some point :) https://pypi.python.org/pypi/bunch – Efren Feb 14 '18 at 00:22
  • 1
    @AlexMartelli Except that dynamically slapping more attributes onto some object that happens to be a callable has nothing to do with lambda calculus and is by all means "hacky" - abusing some random object that had no intention of being a dictionary for you, but just so happens to work. Not that I don't like lambda calculus. I really do. – Elliot Cameron Jan 24 '19 at 06:35
  • @AlexMartelli The way Python's lambda is being used here has nothing to do with the way functions work in the lambda calculus. – user76284 May 20 '20 at 22:12
  • This answrer from @AlexMartelli should not be marked as correct: `NameError: name 'someobject' is not defined` – WhyWhat Nov 27 '21 at 13:25
  • 1
    Dude, the OP's question clearly implies that `someobject` has been previously set -- indeed it STARTS by assigning it. – Alex Martelli Nov 28 '21 at 16:30
187

There is types.SimpleNamespace class in Python 3.3+:

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple, typing.NamedTuple could be used for immutable objects. PEP 557 -- Data Classes suggests a mutable alternative.

For a richer functionality, you could try attrs package. See an example usage. pydantic may be worth a look too.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 4
    If you need something that works with Python 2.7, you can also try the `argparse.Namespace` class – RolKau Aug 26 '16 at 20:09
  • Agreed - I'd be curious if there is a downside here, but this is an incredibly handy python 3.3+ affordance. – ghukill Jul 28 '17 at 18:31
  • damn! this is not available on 2.7? – Shift 'n Tab Sep 11 '18 at 08:00
  • @Roel `attrs` package supports Python 2.7 – jfs Sep 11 '18 at 17:27
  • This seems a better solution to me than unittest.mock; the latter is a bit too heavy-weight and a bit more malleable. With a mock object, simply assigning to an attribute will cause it to spring into existence; SimpleNamespace will resist that. – jdzions Feb 04 '20 at 00:14
56

The mock module is basically made for that.

import mock
obj = mock.Mock()
obj.a = 5
Dunatotatos
  • 1,706
  • 15
  • 25
56

You can also use a class object directly; it creates a namespace:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
MD. Khairul Basar
  • 4,976
  • 14
  • 41
  • 59
Ernesto
  • 1,039
  • 9
  • 14
35

There are a few ways to reach this goal. Basically you need an object which is extendable.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
mbarkhau
  • 8,190
  • 4
  • 30
  • 34
evilpie
  • 2,718
  • 20
  • 21
25

Now you can do (not sure if it's the same answer as evilpie):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
andreabedini
  • 1,295
  • 1
  • 13
  • 20
  • @evilpie's answer sets attributes directly on MyObject (the class), not its instance like yours. – jfs Nov 30 '15 at 15:04
22

Try the code below:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']
Ellis Percival
  • 4,632
  • 2
  • 24
  • 34
neldor
  • 231
  • 2
  • 2
  • 1
    Can you explain more of what this does? while the code may be useful for solving this problem, having it explained can go a lot further than just one problem. – DeadChex Jul 23 '15 at 20:52
  • 2
    @DeadChex Clearly it creates a new Class(object) which is a empty class with object properties, and stores the attributes inside the class. This is even better than install more modules, or rely in lambdas. – m3nda May 15 '17 at 23:08
  • 2
    Not sure why this does not have more upvotes. Is there a reason not to use this for a basic container class? Seems to work fine in Python 2.7, 2.6, and 3.4 – user5359531 Jun 14 '17 at 17:21
  • So how does this set the value of an attribute whose name is contained in a separate variable? – GLRoman Aug 31 '21 at 04:41
10

as docs say:

Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.

You could just use dummy-class instance.

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
4

These solutions are very helpful during testing. Building on everyone else's answers I do this in Python 2.7.9 (without staticmethod I get a TypeError (unbound method...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4
Robpol86
  • 1,594
  • 21
  • 18
3

If we can determine and aggregate all the attributes and values together before creating the nested object, then we could create a new class that takes a dictionary argument on creation.

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

We can also allow keyword arguments. See this post.

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')
HarlemSquirrel
  • 8,966
  • 5
  • 34
  • 34
1

Which objects are you using? Just tried that with a sample class and it worked fine:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

And I got 123 as the answer.

The only situation where I see this failing is if you're trying a setattr on a builtin object.

Update: From the comment this is a repetition of: Why can't you add attributes to object in python?

Community
  • 1
  • 1
jneves
  • 1,194
  • 7
  • 7
1

I think the easiest way is through the collections module.

import collections
FinanceCtaCteM = collections.namedtuple('FinanceCtaCte', 'forma_pago doc_pago get_total')
def get_total(): return 98989898
financtacteobj = FinanceCtaCteM(forma_pago='CONTADO', doc_pago='EFECTIVO',
                                get_total=get_total)

print financtacteobj.get_total()
print financtacteobj.forma_pago
print financtacteobj.doc_pago
Pjl
  • 1,752
  • 18
  • 21
1

if you are looking for chain assignment, to do things such as django model template abstract attribute assigning:

from types import SimpleNamespace


def assign(target, *args, suffix):
    ls = target
    for i in range(len(args) - 1):
        a = args[i]
        ns = SimpleNamespace()
        setattr(ls, a, ns)
        ls = ns
    setattr(ls, args[-1], suffix)
    return ls


a = SimpleNamespace()
assign(a, 'a', 'b', 'c', suffix={'name': 'james'})
print(a.a.b.c)
# {'name': 'james'}

which allows you to pass model as a target, and assign end attribute to it.

Weilory
  • 2,621
  • 19
  • 35
0

Coming to this late in the day but here is my pennyworth with an object that just happens to hold some useful paths in an app but you can adapt it for anything where you want a sorta dict of information that you can access with getattr and dot notation (which is what I think this question is really about):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

This is cool because now:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

So this uses the function object like the above answers but uses the function to get the values (you can still use (getattr, x_path, 'repository') rather than x_path('repository') if you prefer).

Paul Whipp
  • 16,028
  • 4
  • 42
  • 54
0

This works just fine:

    exec("obj.a."+p)

If you want to set the attribute to some value, do this:

    exec("obj.a."+p+"=(the value here)")

For the value to be a string you will have to use these \" instead of quotation marks unless you have the value stored in a variable.

0

I just wanted to create an object from a dictionary.

from pprint import pprint as pp

class Struct(object):

def __init__(self, dict):
    for key in dict.keys():
        self.__setattr__(key, dict[key])


if __name__ == "__main__":
    x = Struct({'a': 1, 'b': 2})
    pp(x.a)
    pp(x.b)`enter code here

Using python 3.11

Darrel Lee
  • 2,372
  • 22
  • 22
-1
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")
lmokto
  • 131
  • 9
-2

Other way i see, this way:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)
leota
  • 1,636
  • 2
  • 13
  • 33