A simple recipe for building tuple-like classes with attribute acessors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | def superTuple(name, attributes):
"""Creates a Super Tuple class."""
dct = {}
#Create __new__.
nargs = len(attributes)
def _new_(cls, *args):
if len(args) != nargs:
raise TypeError("%s takes %d arguments (%d given)." % (cls.__name__,
nargs,
len(args)))
return tuple.__new__(cls, args)
dct["__new__"] = staticmethod(_new_)
#Create __repr__.
def _repr_(self):
contents = [repr(elem) for elem in self]
return "%s<%s>" % (self.__class__.__name__,
", ".join(contents))
dct["__repr__"] = _repr_
#Create attribute properties.
def getter(i):
return lambda self: self.__getitem__(i)
for index, attribute in enumerate(attributes):
dct[attribute] = property(getter(index))
#Set slots.
dct["__slots__"] = []
#Return class.
return type(name, (tuple,), dct)
|
Data is often passed around via tuples, with tuples playing the role of structs or simple-minded records. But remembering what field corresponds to what position can be a real PITA. This recipe attempts to solve that situation.
The recipe is very simple and consists in a unique factory function. As arguments it takes the name of the class and a list of attributes's names. The following simple example illustrates it's use. First we create the class:
>>> Point = superTuple("Point", ["x", "y"])
>>> Point
>>> Point.mro()
[, , ]
>>> Point.__name__
'Point'
So far, so good. Now we create an instance:
>>> p = Point(1, 2, 3)
Traceback (most recent call last):
File "", line 1, in ?
File "C:\Gonçalo\Programming\Python\Code\Library\Containers\superTuple.py", line 16, in _new_
raise TypeError("%s takes %d arguments (%d given)." % (cls.__name__,
TypeError: Point takes 2 arguments (3 given).
Our constructor is working OK.
>>> p = Point(1, 2)
>>> p
>>> p.x
1
>>> p.y
2
As far as implementation goes we just build __new__ and __repr__ methods and for each attribute in the argument attributes list we make a read-only property fetching the corresponding element in the tuple. Then we just call the type constructor with the correct arguments. We have also made __slots__ to be an empty list. In general, I dislike the __slots__ hack, but in this case, since superTuple's are tuple subclasses anyway I figured it was the best thing to do -- all internal state is kept in the tuple elements.
some text was eaten... Some of the text was eaten. For example, Point.mro() was munged. It's all those greater-than and less-than signs being mistaken for HTML tags.
Alternate Implementation. This has a slightly different interface, but is simple and fast:
If the field names need to be visible, there is a more direct approach: