John
John

Reputation: 21917

How can I create an object and add attributes to it?

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

Answers (19)

crazy2be
crazy2be

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:

  1. If your existing code is using dictionary syntax (map_data['directions']), you can migrate over to Bunch, and update the existing code gradually.
  2. Debugging is awesome! You can print, pretty print, iterate over keys / values, everything you would want to do with a dict!

Upvotes: 1

jfs
jfs

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

Darrel Lee
Darrel Lee

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

FogleBird
FogleBird

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

evilpie
evilpie

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

Alex Martelli
Alex Martelli

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

Weilory
Weilory

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

Pjl
Pjl

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

HarlemSquirrel
HarlemSquirrel

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

Ernesto
Ernesto

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

Dunatotatos
Dunatotatos

Reputation: 1746

The mock module is basically made for that.

import mock
obj = mock.Mock()
obj.a = 5

Upvotes: 58

Pablo Emmanuel De Leo
Pablo Emmanuel De Leo

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

Paul Whipp
Paul Whipp

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

neldor
neldor

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

Robpol86
Robpol86

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

lmokto
lmokto

Reputation: 129

di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")

Upvotes: -2

andreabedini
andreabedini

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

jneves
jneves

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

SilentGhost
SilentGhost

Reputation: 319511

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.

Upvotes: 10

Related Questions