diff --git a/util/jeep.py b/util/jeep.py index 37b68d6..4583c5f 100644 --- a/util/jeep.py +++ b/util/jeep.py @@ -31,14 +31,19 @@ class Jeep(object): _attributes = ('_sequence',) def __init__(self, seq=[]): - sequence = self._sequence = [] - for item in seq: - if isinstance(item, (list, tuple)) and len(item) == 2: - attr, value = item - sequence.append(attr) - object.__setattr__(self, attr, value) - else: - self.append(item) + if isinstance(seq, Jeep): + self._sequence = list(seq.keys()) + for key, value in seq.items(): + object.__setattr__(self, key, value) + else: + sequence = self._sequence = [] + for item in seq: + if isinstance(item, (list, tuple)) and len(item) == 2: + attr, value = item + sequence.append(attr) + object.__setattr__(self, attr, value) + else: + self.append(item) def __len__(self): return len(self._sequence) @@ -75,7 +80,7 @@ class Jeep(object): return getattr(self, key, _notfound) is not _notfound def keys(self): - return [key for key in self._sequence] + return list(self._sequence) def values(self): return list(self) @@ -94,9 +99,6 @@ class Jeep(object): return value return existing - def index(self, key): - return self._sequence.index(key) - def append(self, obj): self.insert(len(self), obj) @@ -135,9 +137,25 @@ class Jeep(object): def index(self, obj): idx = self.find(obj) if idx < 0: - raise ValueError('list.index(x): x not in list') + raise ValueError('list.index(x): %r not in list' % obj) return idx + def remove(self, *keys): + myKeys = self.keys() + for key in keys: + if key in myKeys: + del self[key] + + def select(self, *keys): + myKeys = self._sequence + self._sequence = [k for k in keys if k in myKeys] + for k in myKeys: + if k not in keys: + super(Jeep, self).__delattr__(k) + + def reorder(self, delta, *keys): + self._sequence = moveByDelta(self._sequence, keys, delta) + class Term(object): """ A simple name/title association that may be put in a jeep object. @@ -152,3 +170,20 @@ class Term(object): def __str__(self): return self.title + +def moveByDelta(objs, toMove, delta): + """ Return the list given by objs re-ordered in a way that the elements + of toMove (which must be in the objs list) have been moved by delta. + """ + result = [obj for obj in objs if obj not in toMove] + if delta < 0: + objs = list(reversed(objs)) + result.reverse() + toMove = sorted(toMove, lambda x,y: cmp(objs.index(x), objs.index(y))) + for element in toMove: + newPos = min(len(result), objs.index(element) + abs(delta)) + result.insert(newPos, element) + if delta < 0: + result.reverse() + return result + diff --git a/util/jeep.txt b/util/jeep.txt index b0141d7..2fec3e2 100644 --- a/util/jeep.txt +++ b/util/jeep.txt @@ -78,6 +78,25 @@ More Dictionary Methods >>> dict(jeep) {'second': 'new second value', 'third': 'third value', 'first': 'first value'} + >>> jeep.setdefault('first', 'new first value') + 'first value' + >>> jeep['first'] + 'first value' + >>> jeep.setdefault('fourth', 'new fourth value') + 'new fourth value' + >>> jeep['fourth'] + 'new fourth value' + >>> len(jeep) + 4 + + >>> del jeep['fourth'] + >>> len(jeep) + 3 + >>> del jeep['fourth'] + Traceback (most recent call last): + ... + ValueError: ...not in list + More Methods and Operators -------------------------- @@ -103,7 +122,7 @@ Sequence Additions with Named Objects ------------------------------------- Objects that have a ``__name__`` attribute can be appended -to a Jeep object as the dictionary key can be obtained from these attribute. +to a Jeep object as the dictionary key can be obtained from this attribute. >>> class Term(object): ... def __init__(self, token, title=None, value=None): @@ -135,7 +154,54 @@ an exception: Constructors ------------ - >>> jeep2 = Jeep((('f', '1st'), ('s', '2nd'), ('t', '3rd'))) +A simple way of creating a Jeep object is using a sequence +of key-value pairs. + + >>> jeep2 = Jeep((('1', '1st'), ('2', '2nd'), ('3', '3rd'))) >>> list(jeep2) ['1st', '2nd', '3rd'] +Using a Jeep object as the argument of the constructor yields a shallow +copy. + + >>> jeep3 = Jeep(jeep2) + >>> jeep3['1'] is jeep2['1'] + True + >>> jeep3['4'] = '4th' + >>> len(jeep3) + 4 + >>> len(jeep2) + 3 + +More in-place changes +--------------------- + +Remove elements in a fault-tolerant way. + + >>> jeep.keys() + ['first', 'term2', 'second', 'term1'] + >>> jeep.remove('third', 'second') + >>> len(jeep) + 3 + >>> jeep.keys() + ['first', 'term2', 'term1'] + >>> 'second' in jeep + False + +Reorder the sequence and remove all elements that are not +in the argument list. + + >>> jeep.select('term1', 'term2', 'nope') + >>> jeep.keys() + ['term1', 'term2'] + >>> 'first' in jeep + False + +You may even reposition a certain element. + + >>> jeep3.keys() + ['1', '2', '3', '4'] + >>> jeep3.reorder(-1, '2', '4') + >>> jeep3.keys() + ['2', '1', '4', '3'] +