I find myself writing this class often in my python code when I need a quick single use class.
class Struct(object):
def __init__( self, **kwargs ):
for
From Python 3.3 and afterwards, you can use types.SimpleNamespace:
>>> import types
>>> foo = types.SimpleNamespace(bar='one', baz=1)
>>> print(foo.bar)
one
>>> foo.baz += 1
>>> foo.novo = 42
The builtin type is roughly equivalent to the following code:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
update
Starting with Python 3.7, you can use the dataclass module:
from dataclasses import dataclass, field
@dataclass
class Struct:
bar: str = field(default='one')
baz: int = field(default=1)
You can use this as follows:
foo = Struct( bar='one', baz=1 )
print(foo.bar)
foo.baz += 1
foo.novo = 42
By default, it incorporates equality testing and a nice looking repr:
>>> foo == Struct(bar='one', baz=2)
True
>>> foo
Struct(bar='one', baz=2)
There is a python recipe for this (It just updates the instance's dict instead of calling setattr) Recipe 52308
class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)
What you have is a perfectly reasonable prototype, but you're right that it doesn't scale.
If you like using them, but want to have a path to better code later, here's what I'd suggest:
every time you do that, subclass Structure:
class Control(Structure): pass
later, when you want a "real" class, replace the superclass with something like strongbox.Strongbox (example usage) that provides that same constructor and attribute interface, but constrains which slots you can fill in.
A discipline like this only costs you one extra line up front, and won't break your code if you want more power later.
class t(dict):
def __init__(self, **kwargs):
for key, value in kwargs.items():
dict.__setitem__(self, key, value)
def __getattr__(self, key):
return dict.__getitem__(self, key)
def __setattr__(self, key, value):
raise StandardError("Cannot set attributes of tuple")
def __setitem__(self, key, value):
raise StandardError("Cannot set attributes of tuple")
def __delitem__(self, key):
raise StandardError("Cannot delete attributes of tuple")
point = t(x=10, y=500, z=-50)
print point.x # 10
print point.y # 500
print point['z'] # -50
print point # {'z': -50, 'y': 500, 'x': 10}
point.x = 100 # StandardError: cannot set attributes of tuple
point.y += 5 # StandardError: cannot set attributes of tuple
point.z = -1 # StandardError: cannot set attributes of tuple
def hypo(x, y, z):
return (x**2 + y**2 + z**2)**0.5
print hypo(point) # TypeError: unsupported operand type(s)
print hypo(**point) # 502.593274925
for k in point.items():
print k # ('y', 500)
# ('x', 10)
# ('z', -50)
for k in point.keys():
print k # x
# y
# z
for k in point.values():
print k # 500
# 10
# -50
print len(point) # 3
print dict(point) # {'y': 500, 'x': 10, 'z': -50}
This is my solution to this problem. Beautiful syntax, immutable (at least without resorting to some nasty object.setattr() gymnastics), lightweight and pretty-printable. Although there is nothing you can do with this that you cannot do with a dict,
point = t(x=10, y=20, z=30)
d = point.x ** 2 + point.y ** 2 + point.z ** 2
has a really nice symmetry with
point = (10, 20, 30)
d = point[0] ** 2 + point[1] ** 2 + point[2] ** 2
and overall is just so much cleaner than
point = {'x': 10, 'y': 20, 'z': 30}
d = point['x'] ** 2 + point['y'] ** 2 + point['z'] ** 2
You may want to look at Records by George Sakkis. It has worked well for me as a "mutable named tuple."