import operator class charseq(object): def __init__(self, obj=''): if isinstance(obj, charseq): self._s = obj._s else: if not isinstance(obj, basestring): obj = str(obj) self._s = tuple(obj) def fromlist(cls, seq): result = cls() result._s = tuple(seq) return result fromlist = classmethod(fromlist) def __str__(self): return ''.join([str(c) for c in self._s]) def __unicode__(self): return u''.join([unicode(c) for c in self._s]) def __repr__(self): return 'charseq(%r)' % (str(self),) ##### general sequence methods ##### def __len__(self): return len(self._s) def __iter__(self): return iter(self._s) def __getitem__(self, index): if isinstance(index, slice): return self.fromlist(self._s[index]) else: return self._s[index] def __add__(self, other): if isinstance(other, basestring): other = charseq(other) if isinstance(other, charseq): return self.fromlist(self._s + other._s) return NotImplemented def __radd__(self, other): if isinstance(other, basestring): other = charseq(other) if isinstance(other, charseq): return self.fromlist(other._s + self._s) return NotImplemented def __iadd__(self, other): result = self.__add__(other) if result is NotImplemented: raise TypeError, ("unsupported operand types for +=: '%s' and '%s'" % (type(self).__name__, type(other).__name__)) return result def __mul__(self, count): try: return self.fromlist(self._s * count) except TypeError: return NotImplemented def __rmul__(self, count): try: return self.fromlist(count * self._s) except TypeError: return NotImplemented def __imul__(self, other): return self.fromlist(self._s * count) def _compare_op(op): def comparison(self, other): if isinstance(other, basestring): other = charseq(other) if isinstance(other, charseq): return op(self._s, other._s) return NotImplemented return comparison __eq__ = _compare_op(operator.eq) __ne__ = _compare_op(operator.ne) __lt__ = _compare_op(operator.lt) __le__ = _compare_op(operator.le) __gt__ = _compare_op(operator.gt) __ge__ = _compare_op(operator.ge) del _compare_op def __hash__(self): return hash(self._s) ##### string methods ##### def __mod__(self, values): if isinstance(values, tuple): return _formatting.format(self, values, None) else: if hasattr(values, '__getitem__') and hasattr(values, 'keys'): return _formatting.format(self, (values,), values) else: return _formatting.format(self, (values,), None) def capitalize(self): if self._s: lst = [self._s[0].upper()] + [c.lower() for c in self._s[1:]] return self.fromlist(lst) else: return self def center(self, width, fillchar=' '): if len(fillchar) != 1: raise ValueError, "fillchar argument must be a single character" d = width - len(self) if d > 0: offset = d // 2 return offset * fillchar + self + (d - offset) * fillchar else: return self def count(self, sub, start=0, end=None): result = 0 while True: i = self.find(sub, start, end) if i < 0: return result result += 1 start = i + len(sub) def decode(self, *args): return charseq(''.join(self._s).decode(*args)) def encode(self, *args): return charseq(''.join(self._s).encode(*args)) def endswith(self, suffix, start=0, end=None): if start < 0: start += len(self) if start < 0: start = 0 if end is None or end > len(self): end = len(self) elif end < 0: end += len(self) if end < 0: end = 0 begin = end - len(suffix) return begin >= start and self[begin:end] == suffix def expandtabs(self, tabsize=8): result = self.fromlist([]) pos = 0 while True: i = self.find('\t', pos) if i < 0: return result + self[pos:] result += self[:pos] + ' '*(tabsize - ((i-pos) % tabsize)) pos = i+1 def find(self, sub, start=0, end=None): if start < 0: start += len(self) if start < 0: start = 0 if end is None: end = len(self) elif end < 0: end += len(self) if end < 0: end = 0 for i in range(start, end-len(sub)+1): if self[i:i+len(sub)] == sub: return i return -1 def index(self, sub, start=0, end=None): result = self.find(sub, start, end) if result < 0: raise ValueError, "substring not found" return result def isalnum(self): for c in self._s: if not c.isalnum(): return False return bool(self._s) def isalpha(self): for c in self._s: if not c.isalpha(): return False return bool(self._s) def isdigit(self): for c in self._s: if not c.isdigit(): return False return bool(self._s) def islower(self): result = False for c in self._s: if c.isupper(): return False result = result or c.islower() return result def isspace(self): for c in self._s: if not c.isspace(): return False return bool(self._s) def istitle(self): return bool(self._s) and self == self.title() def isupper(self): result = False for c in self._s: if c.islower(): return False result = result or c.isupper() return result def join(self, seq): result = self.fromlist([]) sep = bool(self) for x in seq: if sep == 3: result += self sep |= 2 result += x return result def ljust(self, width, fillchar=' '): if len(fillchar) != 1: raise ValueError, "fillchar argument must be a single character" if len(self) >= width: return self else: return self + fillchar*(width-len(self)) def lower(self): return self.fromlist([c.lower() for c in self._s]) def lstrip(self, chars=None): i = 0 if chars is None: while i < len(self._s) and self._s[i].isspace(): i += 1 else: while i < len(self._s) and self._s[i] in chars: i += 1 return self.fromlist(self._s[i:]) def replace(self, old, new, count=None): if count <= 0: return self return new.join(self.split(old, count)) def rfind(self, sub, start=0, end=None): if start < 0: start += len(self) if start < 0: start = 0 if end is None: end = len(self) elif end < 0: end += len(self) if end < 0: end = 0 for i in range(end-len(sub), start-1, -1): if self[i:i+len(sub)] == sub: return i return -1 def rindex(self, sub, start=0, end=None): result = self.rfind(sub, start, end) if result < 0: raise ValueError, "substring not found" return result def rjust(self, width, fillchar=' '): if len(fillchar) != 1: raise ValueError, "fillchar argument must be a single character" if len(self) >= width: return self else: return fillchar*(width-len(self)) + self def rsplit(self, sep=None, maxsplit=0): result = [] pos = len(self) if sep is None: while pos > 0: pos -= 1 if not self[pos].isspace(): end = pos + 1 if maxsplit and len(result) == maxsplit: result.append(self[:end]) break while pos > 0 and not self[pos-1].isspace(): pos -= 1 result.append(self[pos:end]) pos -= 1 else: while True: i = self.rfind(sep, 0, pos) if i < 0: result.append(self[:pos]) break result.append(self[i+len(sep):pos]) pos = i if len(result) == maxsplit: result.append(self[:pos]) break result.reverse() return result def rstrip(self, chars=None): j = len(self._s) if chars is None: while j > 0 and self._s[j-1].isspace(): j -= 1 else: while j > 0 and self._s[j-1] in chars: j -= 1 return self.fromlist(self._s[:j]) def split(self, sep=None, maxsplit=0): result = [] pos = 0 if sep is None: while pos < len(self): if not self[pos].isspace(): start = pos if maxsplit and len(result) == maxsplit: result.append(self[start:]) break pos += 1 while pos < len(self) and not self[pos].isspace(): pos += 1 result.append(self[start:pos]) pos += 1 else: while True: i = self.find(sep, pos) if i < 0: result.append(self[pos:]) break result.append(self[pos:i]) pos += len(sep) if len(result) == maxsplit: result.append(self[pos:]) break return result def splitlines(self, keepends=False): keepends = bool(keepends) result = self.fromlist([]) pos = 0 while True: i = self.find('\n', pos) if i < 0: result += self[pos:] return result result += self[pos:i+keepends] pos = i+1 def startswith(self, prefix, start=0, end=None): if start < 0: start += len(self) if start < 0: start = 0 if end is None: end = len(self) elif end < 0: end += len(self) if end < 0: end = 0 stop = start + len(prefix) return stop <= end and self[start:stop] == prefix def strip(self, chars=None): i = 0 j = len(self._s) if chars is None: while i < j and self._s[i].isspace(): i += 1 while i < j and self._s[j-1].isspace(): j -= 1 else: while i < j and self._s[i] in chars: i += 1 while i < j and self._s[j-1] in chars: j -= 1 return self.fromlist(self._s[i:j]) def swapcase(self): result = [] for c in self._s: if c.isupper(): c = c.lower() elif c.islower(): c = c.upper() result.append(c) return self.fromlist(result) def title(self): result = [] prev_letter = ' ' for c in self._s: if prev_letter.isalpha(): c = c.lower() else: c = c.upper() result.append(c) prev_letter = c return self.fromlist(result) def translate(self, *args): result = [] for c in self._s: c = c.translate(*args) result += c return self.fromlist(result) def upper(self): return self.fromlist([c.upper() for c in self._s]) def zfill(self, width): firstchar = self[:1] if firstchar == '+' or firstchar == '-': return firstchar + self[1:].rjust(width-1, '0') else: return self.rjust(width, '0') # ____________________________________________________________ import _formatting _formatting.setup(charseq) if __name__ == '__main__': s = charseq('hello')