Reputation: 21917
I want to create a dynamic object in Python and then add attributes to it. This didn't work:
obj = object()
obj.somefield = "somevalue"
AttributeError: 'object' object has no attribute 'somefield'
For details on why it doesn't work, see Can't set attributes on instance of "object" class.
Upvotes: 451
Views: 606159
Reputation: 2252
You can use Alex Martelli's venerable Bunch
class. My favourite variety of it looks like
class Bunch(dict):
def __init__(self, **kwds):
self.update(kwds)
self.__dict__ = self
By comparison to the other answers here, this solution allows you to construct such a class inline, as an expression:
map_data = Bunch(
directions=[d for d in parsed_data.directions],
connections={c.start: Bunch(L=c.left, R=c.right) for c in parsed_data.connections})
print(map_data)
Also, because Bunch
is a dict
, this prints something nice!
{'directions': ['R', 'L'], 'connections': {'AAA': {'L': 'BBB', 'R': 'CCC'}, 'BBB': {'L': 'DDD', 'R': 'EEE'}, 'CCC': {'L': 'ZZZ', 'R': 'GGG'}, 'DDD': {'L': 'DDD', 'R': 'DDD'}, 'EEE': {'L': 'EEE', 'R': 'EEE'}, 'GGG': {'L': 'GGG', 'R': 'GGG'}, 'ZZZ': {'L': 'ZZZ', 'R': 'ZZZ'}}}
By comparison, a custom Object
class (such as the current top answer), prints something not as nice:
<__main__.Object object at 0x7025cae62b90>
The fact that you can use Bunch
as an expression also allows you to use it part of a list or dictionary comprehension, as above:
connections={c.start: Bunch(L=c.left, R=c.right) for c in parsed_data.connections})`
This would be quite difficult to do with any of the other solutions posted here.
You can also construct such an object directly from a dictionary:
my_dict = {'directions': 'LRLRLL', 'connections': []}
map_data = Bunch(**my_dict)
print(map_data)
Another big advantage of this solution over any of the other posted solutions is that your object is a dictionary, as well as an object. This means:
map_data['directions']
), you can migrate over to Bunch
, and update the existing code gradually.Upvotes: 1
Reputation: 414079
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.
Upvotes: 194
Reputation: 2460
To create an object from a dictionary:
class Struct(object):
def __init__(self, d):
for key in d.keys():
self.__setattr__(key, d[key])
Usage:
>>> obj = Struct({'a': 1, 'b': 2})
>>> obj.a
1
Upvotes: 2
Reputation: 76762
The built-in object
can be instantiated but can't have any attributes set on it. (I wish it could, for this exact purpose.) This is because it doesn't have a __dict__
to hold the attributes.
I generally just do this:
class Object(object):
pass
obj = Object()
obj.somefield = "somevalue"
But consider giving the Object
class a more meaningful name, depending on what data it holds.
Another possibility is to use a sub-class of dict
that allows attribute access to get at the keys:
class AttrDict(dict):
def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
obj = AttrDict()
obj.somefield = "somevalue"
To instantiate the object attributes using a dictionary:
d = {"a": 1, "b": 2, "c": 3}
for k, v in d.items():
setattr(obj, k, v)
Upvotes: 479
Reputation: 2931
There are a few ways to reach this goal. Basically you need an object which is extendable.
obj = type('Test', (object,), {})
obj.b = 'fun'
obj = lambda:None
obj.b = 'fun'
class Test:
pass
obj = Test()
obj.b = 'fun'
Upvotes: 38
Reputation: 881477
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 = lambda: None
obj.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.
Upvotes: 289
Reputation: 3101
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.
Upvotes: 1
Reputation: 1812
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
Upvotes: 1
Reputation: 10114
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')
Upvotes: 3
Reputation: 1219
You can also use a class object directly; it creates a namespace:
class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
Upvotes: 64
Reputation: 1746
The mock
module is basically made for that.
import mock
obj = mock.Mock()
obj.a = 5
Upvotes: 58
Reputation: 157
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)
Upvotes: -3
Reputation: 16521
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).
Upvotes: 0
Reputation: 241
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']
Upvotes: 23
Reputation: 1631
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
Upvotes: 4
Reputation: 129
di = {}
for x in range(20):
name = '_id%s' % x
di[name] = type(name, (object), {})
setattr(di[name], "attr", "value")
Upvotes: -2
Reputation: 1315
Now you can do (not sure if it's the same answer as evilpie):
MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
Upvotes: 25
Reputation: 1324
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?
Upvotes: 1
Reputation: 319511
as docs say:
Note:
object
does not have a__dict__
, so you can’t assign arbitrary attributes to an instance of theobject
class.
You could just use dummy-class instance.
Upvotes: 10