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.

Powered by Sputnik | XHTML 1.1