It is my thesis that our abstractions aren’t fit for this sort of problem. The problems above “hit it where it hurts” so to speak, because in order to be solved elegantly they require context, and in the one dimensional world of strings this means: what came before? what came after? Which part of the string are we focusing on right now? This is exactly what StringContext is. It contains 3 parts: before , focus , after :

As you can see, slicing has the function of shifting the focus of the string. “I want to look at this part now”. These points are true of StringContext and MatchContext :

MatchContext

MatchContext is what you get when you do regular expression searches. It’s a subclass of StringContext and contains information relevant to the match/search it was created for, namely the “span” - a (start, end) tuple of indices of the string, start and end are both referred to as points - of the various regex groups that happened to match. It also contains useful methods pertaining to these regex groups, like MatchContext.group and MatchContext.expand . Q: But what happens to the regex spans when you manipulate the string, for example with MatchContext.replace ? A: They move around in sensible ways. The details can be found in the docstring for MatchContext.replace , but the gist of it is that if focus grows in length by 3 when you replace it, then any point at the very end or after focus also grows by 3. If the point is before focus then it stays the same. If it is in the middle of focus then it might “shrink” if focus becomes too small to contain it.

This is how you’d use it to solve problem number 2:

>>> contex.match('Vacation2008Photo_034.jpg', r'Vacation2008Photo_(?P<number>[0-9]+)\.jpg') MatchContext('', 'Vacation2008Photo_034.jpg', '') >>> m = contex.match('Vacation2008Photo_034.jpg', r'Vacation2008Photo_(?P<number>[0-9]+)\.jpg') >>> m.group("number") MatchContext('Vacation2008Photo_', '034', '.jpg') >>> m.group('number').replace(lambda num: '{:0>3}'.format(int(num) - 1)) MatchContext('Vacation2008Photo_', '033', '.jpg') >>> result = m.group('number').replace(lambda num: '{:0>3}'.format(int(num) - 1)) >>> str(result) 'Vacation2008Photo_033.jpg'