Shortest way of creating an object with arbitrary attributes in Python?
Question:
Hey, I just started wondering about this as I came upon a code that expected an object with a certain set of attributes (but with no specification of what type this object should be).
One solution would be to create a new class that has the attributes the code expects, but as I call other code that also needs objects with (other) attributes, I’d have to create more and more classes.
A shorter solution is to create a generic class, and then set the attributes on instances of it (for those who thought of using an instance of object
instead of creating a new class, that won’t work since object
instances don’t allow new attributes).
The last, shortest solution I came up with was to create a class with a constructor that takes keyword arguments, just like the dict
constructor, and then sets them as attributes:
class data:
def __init__(self, **kw):
for name in kw:
setattr(self, name, kw[name])
options = data(do_good_stuff=True, do_bad_stuff=False)
But I can’t help feeling like I’ve missed something obvious… Isn’t there a built-in way to do this (preferably supported in Python 2.5)?
Answers:
Use collections.namedtuple
.
It works well.
from collections import namedtuple
Data = namedtuple( 'Data', [ 'do_good_stuff', 'do_bad_stuff' ] )
options = Data( True, False )
If I understand your question correctly, you need records. Python classes may be used this way, which is what you do.
I believe the most pythonic way of dealing with “records” is simply… dictionaries! A class is a sort of dictionary on steroids.
Your class example data
is essentially a way of converting a dictionary into a class.
(On a side note, I would rather use self.__setattr__(name, kw[name])
.)
This is the shortest way I know
>>> obj = type("myobj",(object,),dict(foo=1,bar=2))
>>> obj.foo
1
>>> obj.bar
2
>>>
using dict instead of {} insures your attribute names are valid
>>> obj = type("myobj",(object,),{"foo-attr":1,"bar-attr":2})
>>> obj.foo-attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'myobj' has no attribute 'foo'
>>>
You might be interested in the “Struct”, which is part of the IPython package. It does what you want to do, with lots of useful methods.
http://ipython.org/ipython-doc/rel-0.13/api/generated/IPython.utils.ipstruct.html
This is typically something you would use a dict for, not making a class at all.
This works in 2.5, 2.6, and 3.1:
class Struct(object):
pass
something = Struct()
something.awesome = abs
result = something.awesome(-42)
EDIT:
I thought maybe giving the source would help out as well.
http://docs.python.org/tutorial/classes.html#odds-and-ends
EDIT:
Added assignment to result, as I was using the interactive interpreters to verify, and you might not be.
The original code can be streamlined a little by using __dict__
:
In [1]: class data:
...: def __init__(self, **kwargs):
...: self.__dict__.update(kwargs)
...:
In [2]: d = data(foo=1, bar=2)
In [3]: d.foo
Out[3]: 1
In [4]: d.bar
Out[4]: 2
In Python 3.3 and greater, this syntax is made available by the types.SimpleNamespace
class.
Use a combination between lambda and type build-in, I think is the smallest way to do that:
obj = lambda **kwargs: type('obj', (object,), kwargs)()
options = obj(do_good_stuff=True, do_bad_stuff=False)
print options.do_good_stuff
print options.do_bad_stuff
If you don’t need to pass values in the constructor, you can do this:
class data: pass
data.foo = 1
data.bar = 2
You use the class static member variables to hold your data.
type('', (), {})()
will create an object that can have arbitrary attributes.
Example:
obj = type('', (), {})()
obj.hello = "hello"
obj.world = "world"
print obj.hello, obj.world # will print "hello world"
type()
with three arguments creates a new type.
-
The first argument ''
is the name of the new type. We don’t care about the name, so we leave it empty.
-
The second argument ()
is a tuple of base types. Here object
is implicit.
-
The third argument is a dictionary of attributes of the new object. We start off with no attributes so it’s empty {}
.
In the end we instantiate a new instance of this new type with ()
.
On Python 3.3+, use types.SimpleNamespace
:
>>> from types import SimpleNamespace
>>> foo = SimpleNamespace()
>>> foo.hello = "world"
Hey, I just started wondering about this as I came upon a code that expected an object with a certain set of attributes (but with no specification of what type this object should be).
One solution would be to create a new class that has the attributes the code expects, but as I call other code that also needs objects with (other) attributes, I’d have to create more and more classes.
A shorter solution is to create a generic class, and then set the attributes on instances of it (for those who thought of using an instance of object
instead of creating a new class, that won’t work since object
instances don’t allow new attributes).
The last, shortest solution I came up with was to create a class with a constructor that takes keyword arguments, just like the dict
constructor, and then sets them as attributes:
class data:
def __init__(self, **kw):
for name in kw:
setattr(self, name, kw[name])
options = data(do_good_stuff=True, do_bad_stuff=False)
But I can’t help feeling like I’ve missed something obvious… Isn’t there a built-in way to do this (preferably supported in Python 2.5)?
Use collections.namedtuple
.
It works well.
from collections import namedtuple
Data = namedtuple( 'Data', [ 'do_good_stuff', 'do_bad_stuff' ] )
options = Data( True, False )
If I understand your question correctly, you need records. Python classes may be used this way, which is what you do.
I believe the most pythonic way of dealing with “records” is simply… dictionaries! A class is a sort of dictionary on steroids.
Your class example data
is essentially a way of converting a dictionary into a class.
(On a side note, I would rather use self.__setattr__(name, kw[name])
.)
This is the shortest way I know
>>> obj = type("myobj",(object,),dict(foo=1,bar=2))
>>> obj.foo
1
>>> obj.bar
2
>>>
using dict instead of {} insures your attribute names are valid
>>> obj = type("myobj",(object,),{"foo-attr":1,"bar-attr":2})
>>> obj.foo-attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'myobj' has no attribute 'foo'
>>>
You might be interested in the “Struct”, which is part of the IPython package. It does what you want to do, with lots of useful methods.
http://ipython.org/ipython-doc/rel-0.13/api/generated/IPython.utils.ipstruct.html
This is typically something you would use a dict for, not making a class at all.
This works in 2.5, 2.6, and 3.1:
class Struct(object):
pass
something = Struct()
something.awesome = abs
result = something.awesome(-42)
EDIT:
I thought maybe giving the source would help out as well.
http://docs.python.org/tutorial/classes.html#odds-and-ends
EDIT:
Added assignment to result, as I was using the interactive interpreters to verify, and you might not be.
The original code can be streamlined a little by using __dict__
:
In [1]: class data:
...: def __init__(self, **kwargs):
...: self.__dict__.update(kwargs)
...:
In [2]: d = data(foo=1, bar=2)
In [3]: d.foo
Out[3]: 1
In [4]: d.bar
Out[4]: 2
In Python 3.3 and greater, this syntax is made available by the types.SimpleNamespace
class.
Use a combination between lambda and type build-in, I think is the smallest way to do that:
obj = lambda **kwargs: type('obj', (object,), kwargs)()
options = obj(do_good_stuff=True, do_bad_stuff=False)
print options.do_good_stuff
print options.do_bad_stuff
If you don’t need to pass values in the constructor, you can do this:
class data: pass
data.foo = 1
data.bar = 2
You use the class static member variables to hold your data.
type('', (), {})()
will create an object that can have arbitrary attributes.
Example:
obj = type('', (), {})()
obj.hello = "hello"
obj.world = "world"
print obj.hello, obj.world # will print "hello world"
type()
with three arguments creates a new type.
-
The first argument
''
is the name of the new type. We don’t care about the name, so we leave it empty. -
The second argument
()
is a tuple of base types. Hereobject
is implicit. -
The third argument is a dictionary of attributes of the new object. We start off with no attributes so it’s empty
{}
.
In the end we instantiate a new instance of this new type with ()
.
On Python 3.3+, use types.SimpleNamespace
:
>>> from types import SimpleNamespace
>>> foo = SimpleNamespace()
>>> foo.hello = "world"