Search code examples
pythonclassextend

Can you shortcut a python method like string replace?


In Python, you can say this:

s = 'fred loves mary'
r = s.replace
s = r('fred','ben')
print s

...and you get, very handily, sort of:

ben loves mary

However, because when you said r = s.replace you created an object that always contains 'fred loves mary', this doesn't produce "ben loves deb"...

s = 'fred loves mary'
r = s.replace
s = r('fred','ben')
s = r('mary','deb')
print s

...instead, it produces:

fred loves deb

I understand what's happening. But the ability to actually write quick code like the latter set and have it work, that is, produce:

ben loves deb

...would be awesome. So my question is, is there a way to do that other than defining ObviousFunction(s,x,y) or a class, viz:

class xs:
    def __init__(self,s):
        self.s = s
    def r(self,x,y):
        self.s = self.s.replace(x,y)
    def __str__(self):
        return self.s 


s = xs('fred loves mary')
s = s.r('mary','deb')
s = s.r('fred','ben')
print s

...which actually does produce:

ben loves deb

...but for further string ops, requires the following type of gymnastics:

print s.s.upper()

...

BEN LOVES DEB

I took a look for any ability to extend an existing class (str in this case), but came up dry. Weak google-fu?

Hmmm. Ok, I got this far:

class xs(str)
    def r(self,x,y):
        return xs(self.replace(x,y))

...which will do this:

s = xs('fred loves mary')
s = s.r('fred','ben')
s = s.r('mary','deb')
# so far, so good; s is still class xs and you can go on forever
# but if you do this:
s = s.replace('ben','meezer')
# now s is an object of class str

...so is there any way to coerce a return value generally to the original class, as I've coerced the value .r() is returning? Other than just saying...

s = xs(s)

...afterwards?

I can see that one could extend this class until one had managed to wrap every type of string call that returns a string and then toss the (largish) result in one's toolbox, but that's a lot of work for something that seems like it ought to be easy. Still, it might be the only solution, and if so... I'm definitely going to end up with such a class. The number of things class str could gain are legion, quite aside from the benefits of shortening all that wordiness so the work/character factor increases.

Thanks for any insight.


Solution

  • Ok, after all the assistance here and some further trolling about the net, I have something that is very close to what I wanted. I've written a much more complete class, but the following snippets exercise the issues directly associated with my original inquiry:

    class xs(str):
        def __add__(self,a):            # OPERATORS
            return xs(str(self) + a)    # +
        def __mod__(self,tup):
            return xs(str(self) % tup)  # %
        def __getslice__(self,b,c):
            return xs(str(self)[b:c])   # [:]
    
        def r(self,x,y):                # replace, as 'r'
            return xs(self.replace(x,y))
    

    Using the above, class identity is maintained, the ability to code up replacements for every class str function that returns a string is there, and everything else works just the way it does with strings. so...

    a = xs('fred loves mary')
    a = a.r('fred','ben')
    a = a.r('mary','deb')
    print a
    

    ...results in...

    ben loves deb
    

    ...and...

    b = a[:3]  # get 'ben'
    c = a[-3:] # get 'deb'
    d = xs('%s adores %s') # format string
    e = d % (b,c) # do the formatting
    e = e.r('ben','charlie')
    f = e + ' (so I hear)'
    print f,str(type(f))
    

    ...yields...

    charlie adores deb (so I hear)  <class '__main__.xs'>
    

    So as we can see, class xs is maintained throughout, standard operations apply as per usual. Anything that's in class str that doesn't return a string can remain unmolested; anything that does should be taken care of as .replace() is here. Objects of class xs can be used pretty much anywhere a string can, and in many cases, strings can be used with objects of class xs without causing problems; notable exception is the first operand of + and %, as these must be of class xs in order for the operator overrides to get hold of them in the first place.

    What would have been best would have been the ability to extend class str. But subclassing it provides most of the benefits.

    If anyone is actually interested, I'll post the full class (mostly complete as of now) somewhere you can grab it. Includes dotted quad string handiness, class exceptions, yadda yadda. Running under Python 2.6.1, would require additional work for Python 3.

    Thanks to everyone for the help!