#!/usr/bin/env python """ table.py - Pluvo Table Datatype Author: Sean B. Palmer, inamidst.com """ # from number import Number def slicerange(s, length=None): if (not isinstance(s, slice)) or (s.start == s.stop): raise ValueError("Expected non-zero sized slice object") return xrange(s.start, s.stop or length, s.step or 1) class Table(dict): """A List-Dictionary Hybrid. Cf. Javascript, and Lua. The constructor acts like dict(...) unless a list is passed: Table(list) -> new list-dictionary made with list array sequence """ def __init__(self, *args, **kargs): self.__length = 0 if (len(args) == 1) and isinstance(args[0], list): members = args[0] args = tuple() else: members = False super(Table, self).__init__(*args, **kargs) for key in self.iterkeys(): if isinstance(key, int): nlen = key + 1 if nlen > self.__length: self.__length = nlen if members: self.extend(members) def __str__(self): keys = [] itemkeys = [] for key in sorted(self.keys()): if isinstance(key, int): itemkeys.append(key) else: keys.append(key) result = ["("] for key in keys: result.append(str(key)) result.append('=') try: result.append(str(self[key])) except: result.append('@@') result.append(', ') for itemkey in itemkeys: result.append(str(self[itemkey])) result.append(' ') if result[-1] == ' ': result = result[:-1] result.append(")") return "".join(result) def __len__(self): """Get the length of the Table as a list.""" return self.__length def dictlen(self): """Get the length of the Table as a dict.""" return super(Table, self).__len__() def __eq__(self, obj): """Compare objects based on type.""" if isinstance(obj, list): return list(self).__eq__(obj) elif not isinstance(obj, Table): return False elif self.__cmp__(obj) == 1: return True return False def __ne__(self, obj): """Compare objects based on type.""" if isinstance(obj, list): return list(self).__ne__(obj) elif self.__cmp__(obj) == 1: return False return True def __add__(self, obj): if not isinstance(obj, Table): ValueError("Object is not a Table.") result = Table(self) result.extend(obj) return result def __iadd__(self, obj): if not isinstance(obj, Table): ValueError("Object is not a Table.") self.extend(obj) return self def __mul__(self, i): result = Table(self) result.extend(list(self) * (i - 1)) return result def __imul__(self, i): extension = list(self) * (i - 1) self.extend(extension) return self def __rmul__(self, i): self.__mul__(i) def __contains__(self, obj): """Contains as a list.""" # Use Table.has_key for dict-like behaviour for item in self: if item == obj: return True return False def __iter__(self): """Iterate over the Table as a list.""" for i in xrange(self.__length): yield self[i] def __getitem__(self, item): # @@ Number if isinstance(item, int): # @@ long, float return self.get(item) elif isinstance(item, slice): if item.start != item.stop: # was: Table(list(self)[item]) result = Table() # @@ when more people upgrade, use listless syntax result.extend([self[i] for i in slicerange(item, len(self))]) return result else: return Table() return super(Table, self).__getitem__(item) def __setitem__(self, key, item): if not isinstance(key, slice): if isinstance(key, int): nlen = key + 1 if nlen > self.__length: self.__length = nlen super(Table, self).__setitem__(key, item) elif key.start != key.stop: if key.stop != self.__length: memo = self[key.stop:self.__length] # @@ as list! else: memo = [] rest = xrange(key.stop, self.__length) self.truncate(key.start) self.extend(item) self.extend(memo) else: length = len(item) for i in xrange(key.start, self.__length): obj = self[i] del self[i] self[i + length] = obj for (i, obj) in enumerate(item): self[i + key.start] = obj def __delitem__(self, key): if isinstance(key, int): if key == self.__length - 1: self.__length -= 1 elif key >= self.__length: raise KeyError("Index out of bounds.") if self.has_key(key): super(Table, self).__delitem__(key) def copy(self): import copy new = Table() for key, value in self.iteritems(): try: key = copy.deepcopy(key) except: pass if isinstance(value, Table): value = value.copy() else: try: value = copy.deepcopy(value) except: pass new[key] = value # for item in self: # if isinstance(item, Table): # item = item.copy() # else: # try: item = copy.copy(item) # except: pass # new.append(item) assert len(new) == len(self) return new def count(self, obj): result = 0 for i in range(0, self.__length): if self[i] == obj: result += 1 return result def append(self, obj): # __setitem__ increments the length itself self[self.__length] = obj def extend(self, objs): for obj in objs: self.append(obj) def delete(self, i): """Delete index as a list.""" # print 'delete(%s, %s) -> ' % (self, i), if i >= self.__length: raise ValueError("Index out of bounds.") del self[i] for n in xrange(i + 1, self.__length): obj = self[n] del self[n] self[n - 1] = obj # print self def index(self, obj, i=None, j=None): if i is None: i = 0 if j is None: j = self.__length for n in xrange(i, j): if self[n] == obj: return n raise ValueError("Table.index(obj): obj not in list") def insert(self, i, item): self[i:i] = [item] def pop(self): lastidx = self.__length - 1 result = self[lastidx] del self[lastidx] return result def remove(self, i): self.delete(self.index(i)) def truncate(self, i): """Truncate to min(i, len(table)) entries as a list.""" i = min(i, self.__length) for n in xrange(i, self.__length): del self[n] self.__length = i def sort(self): for (i, item) in enumerate(sorted(self)): # @@ does anything sort before None? self[i] = item def reverse(self): for (i, item) in enumerate(reversed(self)): # @@ only set final marker None self[i] = item def main(): pass if __name__ == '__main__': main()