new string function suggestion

Andrew Dalke dalke at dalkescientific.com
Mon Jun 13 09:49:44 CEST 2005


Andy wrote:
> What do people think of this?
> 
> 'prefixed string'.lchop('prefix') == 'ed string'
> 'string with suffix'.rchop('suffix') == 'string with '
> 'prefix and suffix.chop('prefix', 'suffix') == ' and '

Your use case is

> I get tired of writing stuff like:
> 
> if path.startswith('html/'):
>   path = path[len('html/'):]
> elif s.startswith('text/'):
>   path = path[len('text/'):]
> 
> It just gets tedious, and there is duplication.  Instead I could just write:
> 
> try:
>   path = path.lchop('html/')
>   path = path.lchop('text/')
> except SomeException:
>   pass

But your posted code doesn't implement your use case.  Consider
if path == "html/text/something".  Then the if/elif code sets
path to "text/something" while the lchop code sets it to "something".

One thing to consider is a function (or string method) which
is designed around the 'or' function, like this.  (Named 'lchop2'
but it doesn't give the same interface as your code.)

def lchop2(s, prefix):
  if s.startswith(prefix):
    return s[len(prefix):]
  return None

path = lchop2(path, "html/") or lchop2(path, "text/") or path


If I saw a function named "lchop" (or perhaps named "lchomp") I
would expect it to be (named 'lchomp3' so I can distinguish
between it and the other two)

def lchop3(s, prefix):
  if s.startswith(prefix):
    return s[len(prefix):]
  return s

and not raise an exception if the prefix/suffix doesn't match.
Though in this case your use case is not made any simpler.
Indeed it's uglier with either

newpath = path.lchop3("html/")
if newpath == path
  newpath = path.lchop3("text/")
  if newpath == path:
    ...

or

if path.startswith("html/"):
  path = path.lstrip("html/")
elif path.startswith("text/"):
  path = path.lstrip("text/")
   ...



I tried finding an example in the stdlib of code that would be
improved with your proposal.  Here's something that would not
be improved, from mimify.py (it was the first grep hit I
looked at)

        if prefix and line[:len(prefix)] == prefix:
            line = line[len(prefix):]
            pref = prefix
        else:
            pref = ''

In your version it would be:

        if prefix:
            try:
                line = line.rstrip(prefix)
            except TheException:
                pref = ''
            else:
                pref = prefix
        else:
            pref = ''

which is longer than the original.

 From pickle.py (grepping for 'endswith(' and a context of 2)

pickle.py-    if ashex.endswith('L'):
pickle.py:        ashex = ashex[2:-1]
pickle.py-    else:
pickle.py:        ashex = ashex[2:]

this would be better with my '3' variant, as

  ashex = ashex.rchop3('L')[2:]

while your version would have to be

  try:
    ashex = ashex.rchomp('L')[2:]
  except SomeException:
    ashex = ashex[2:]


Even with my '2' version it's the simpler

  ashex = (ashex.rchop2('L') or ashex)[2:]

The most common case will be for something like this

tarfile.py-        if self.name.endswith(".gz"):
tarfile.py-            self.name = self.name[:-3]

My "3" code handles it best

  self.name = self.name.rstrip3(".gz")

Because your code throws an exception for what isn't
really an exceptional case it in essence needlessly
requires try/except/else logic instead of the simpler
if/elif logic.

> Does anyone else find this to be a common need?  Has this been suggested 
> before?

To summarize:
  - I don't think it's needed that often
  - I don't think your implementation's behavior (using an
       exception) is what people would expect
  - I don't think it does what you expect

				Andrew
				dalke at dalkescientific.com




More information about the Python-list mailing list