struct for Python
C structs are great -- they let you allocate structure a little chunk of memory so that an integer goes here, a float goes there, give names to each component, and efficiently use it.
There's a bunch of ways to simulate that in Python, such as using a tuple (efficient, but loses the names), or a dictionary (mystruct["member"] is awkward, and not very efficient) or the dict member of an object (nicer referencing, but still inefficient and somewhat annoying to construct).
The slots features provides a nice answer -- it lets you have a class-like interface, with tuple-like efficiency; and also gives you errors if you try adding new attributes at runtime, which is a sign you want more than a struct afterall.
(An alternative way of looking at it is this provides mutable named tuples)
Here's how you do it. First, a base class, with methods to initialise an object and compare it in a tuple-like manner:
class Struct(object):
__slots__ = ()
def __init__(self, *args, **kwargs):
d = dict(zip(self.__slots__, args))
d.update(kwargs)
for x in self.__slots__:
setattr(self, x, d.get(x,None))
def __cmp__(self, other):
if not hasattr(other,"__slots__"):
return -1 # BUG -- should do something more intelligent :(
if self.__slots__ != other.__slots__:
return cmp(self.__slots__, other.__slots__)
for s in self.__slots__:
c = cmp(getattr(self,s), getattr(other,s))
if c != 0: return c
return 0
Usage is then to declare what fields the structure has:
class Xyzzy(Struct):
__slots__ = ("foo", "bar", "baz")
and use it:
>>> x1 = Xyzzy()
>>> x2 = Xyzzy(foo=5, bar=6, baz=7)
>>> x3 = Xyzzy(5, 6, 7)
>>> x4 = Xyzzy(5, baz=7)
>>> [a.foo for a in [x1,x2,x3,x4]]
[None, 5, 5, 5]
>>> [a.bar for a in [x1,x2,x3,x4]]
[None, 6, 6, None]
>>> [a.baz for a in [x1,x2,x3,x4]]
[None, 7, 7, 7]
>>> Xyzzy(1,2,3)==Xyzzy(1,2,3) # same value?
True
>>> Xyzzy(1,2,3) is Xyzzy(1,2,3) # same object?
False
>>> cmp(Xyzzy(1,2,3),Xyzzy(1,3,3))
-1
>>> cmp(Xyzzy(1,2,3),Xyzzy(1,1,3))
1
Neato.