#!/usr/bin/env python # encoding: utf-8 """ relaxed.py Created by Patrick Thomson on 2007-07-12. This is licensed under the WTFPL. Offers a relaxed, Ruby-esque version of a list. Features include: * Accessing non-existent elements returns None instead of an error * Setting elements out of the array's range appends None as needed * Strict and non-strict flattening algorithms * first(), last(), join() Thanks to: http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks for flatten() Implementation notes: Instead of extending the built-in list type, I wrote a simple object that wraps an encapsulated list. Why? Check the Python documentation: http://docs.python.org/ref/sequence-types.html for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence. Since the point of this was to have a container that raised as few errors as possible, I chose to have an object serving as a frontend for the list that intercepted invalid indexes. I know that it isn't pretty, but it's the best I could do. """ import unittest class rlist(object): @classmethod def list_flatten_strictly(a_list): result = [] for ii in self.__internal: if hasattr(ii, '__iter__'): result.extend(flatten(ii)) else: result.append(ii) return result @classmethod def list_flatten_softly(a_list): result = [] for ii in self.__internal: if hasattr(ii, '__iter__') and isinstance(ii, (list, tuple)): result.extend(flatten(ii)) else: result.append(ii) return result def __init__(self, *args): if isinstance(args[0], rlist): self.__internal = args[0].contents if (len(args) == 1) and (isinstance(args[0], list)): self.__internal = args[0] else: self.__internal = list(args) def __len__(self): return len(self.__internal) def __getitem__(self, key): try: return self.__internal[key] except IndexError, e: return None def __setitem__(self, key, value): while True: try: self.__internal[key] = value break except IndexError, e: # Iteratively adds Nones until filled out properly self.append(None) def __delitem__(self, key): try: del self.__internal[key] except IndexError, e: # Do nothing if user tries to delete nonexistent item pass def __contains__(self, item): return (item in self.__internal) def __iter__(self): return iter(self.__internal) def __add__(self, other): return self.__internal + other def __iadd__(self, other): self.__internal += other def __mul__(self, other): return self.__internal * other def __imul__(self, other): self.__internal *= other def __getattr__(self, attr): if hasattr(self.__internal, attr): return getattr(self.__internal, attr) else: raise AttributeError, "'RelaxedList' object has no attribute '%s'" % attr def __repr__(self): return str(self.__internal) def __str__(self): return 'RelaxedList: %s' % str(self.__internal) def __cmp__(self, other): return self.__internal == other def as_list(self): return self.__internal def is_empty(self): return len(self) == 0 def strictly_flattened(self): return RelaxedList(rlist.list_flatten_strictly(self.__internal)) def flatten_strictly(self): self.__internal = rlist.list_flatten_strictly(self.__internal) flattened = strictly_flattened flatten = flatten_strictly def softly_flattened(self): return RelaxedList(rlist.list_flatten_softly(self.__internal)) def flatten_softly(self): self.__internal = rlist.list_flatten_softly(self.__internal) def first(self): return self[0] def last(self): return self[len(self) - 1] def join(self, str): return str.join(self.__internal) def contents(): doc = "The attributes of the RelaxedList" def fget(self): return self.__internal return locals() contents = property(**contents()) class RelaxedListTests(unittest.TestCase): def setUp(self): self.first = rlist() self.second = rlist([1, 2, 3, 4000]) self.third = rlist("Would", "you", "stand", "up", "please", "?") def testEquality(self): self.assertEqual(self.first, rlist()) self.assertEqual(self.second, rlist([1, 2, 3, 4000])) self.assertEqual(self.third, rlist("Would", "you", "stand", "up", "please", "?")) def testContents(self): self.assertEqual(self.first.contents, []) self.assertEqual(self.second.contents, [1, 2, 3, 4000]) self.assertEqual(self.third.contents, ["Would", "you", "stand", "up", "please", "?"]) def testGetItems(self): self.assertEqual(self.second[2], 3) self.assertEqual(self.third[3], "up") def testSetItems(self): self.second[3] = "my hovercraft!" self.assertEqual(self.second.contents, [1, 2, 3, 'my hovercraft!']) self.second[3] = 4000 self.assertEqual(self.second, rlist([1, 2, 3, 4000])) def testAppendAndPop(self): self.second.append("it is full of eels!") self.assertEqual(self.second.contents, [1, 2, 3, 4000, "it is full of eels!"]) meh = self.second.pop() self.assertEqual(meh, "it is full of eels!") self.assertEqual(self.second.contents, [1, 2, 3, 4000]) def testIsEmpty(self): self.assert_(self.first.is_empty(), "What? First isn't empty?") if __name__ == '__main__': unittest.main()