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.
Download
Copy to clipboard
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: