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?
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.
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)
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.
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.
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:-).
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.
argparse.Namespace
class
attrs
package supports Python 2.7
The mock
module is basically made for that.
import mock
obj = mock.Mock()
obj.a = 5
unittest.Mock
is a part of the standard library since Python 3.3 (docs.python.org/3/library/unittest.mock.html)
mock
in it. Just feels weird to me.
You can also use a class object directly; it creates a namespace:
class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
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()
obj.a = type('', (), {})
Now you can do (not sure if it's the same answer as evilpie):
MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
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']
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.
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
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')
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?
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
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).
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.
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.
di = {}
for x in range(20):
name = '_id%s' % x
di[name] = type(name, (object), {})
setattr(di[name], "attr", "value")
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)
class a: pass
gives all the power required.
Success story sharing
foo = object()
works, but you just can't do much of anything with ittype....
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.Struct
as the name of the class to make it more obvious. Saved me a ton of typing["
and"]
, Cheers!