By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,036 Members | 1,963 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,036 IT Pros & Developers. It's quick & easy.

new string function suggestion

P: n/a
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 '

The names are analogous to strip, rstrip, and lstrip. But the functionality
is basically this:

def lchop(self, prefix):
assert self.startswith(prefix)
return self[len(prefix):]

def rchop(self, suffix):
assert self.endswith(suffix)
return self[:-len(suffix]

def chop(self, prefix, suffix):
assert self.startswith(prefix)
assert self.endswith(suffix)
return self[len(prefix):-len(suffix]

The assert can be a raise of an appropriate exception instead. I find this
to be a very common need, and often newbies assume that the
strip/lstrip/rstrip family behaves like this, but of course they don't.

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

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

Andy
Jul 19 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
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
da***@dalkescientific.com

Jul 19 '05 #2

P: n/a
On Mon, 13 Jun 2005 07:05:39 +0000, 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 '

The names are analogous to strip, rstrip, and lstrip.
If chop is analogous to strip, then they shouldn't raise exceptions if the
strings do not start with the prefix or suffix.
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:


Any time you are writing the same piece of code over and over again, you
should refactor it out as a function in your module, or even in a special
"utility functions" module:

def lchop(s, prefix):
if s.startswith(prefix): return s[len(prefix):]
raise ValueError("Substring doesn't begin with prefix.")
# or just return s if you prefer

and then call them whenever you need them.
--
Steven.

Jul 19 '05 #3

P: n/a
[Andrew Dalke]
<200 lines of thorough analysis>
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


Wow, that was a remarkably thoughtful, total demolition ;-)
I expect that this thread is going to be somewhat short lived.

Jul 19 '05 #4

P: n/a
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 '

The names are analogous to strip, rstrip, and lstrip. But the functionality
is basically this:

def lchop(self, prefix):
assert self.startswith(prefix)
return self[len(prefix):]

def rchop(self, suffix):
assert self.endswith(suffix)
return self[:-len(suffix]

def chop(self, prefix, suffix):
assert self.startswith(prefix)
assert self.endswith(suffix)
return self[len(prefix):-len(suffix]

The assert can be a raise of an appropriate exception instead. I find this
to be a very common need,
I'm not sure whether I should be surprised or not. I've never felt the
need for such a gadget. I don't even recall seeing such a gadget in
other languages. One normally either maintains a cursor (index or
pointer) without chopping up the original text, or splits the whole text
up into tokens. AFAICT, most simple needs in Python are satisfied by
str.split or re.split. For the special case of file paths, see
os.path.split*. There are also various 3rd party parsing modules -- look
in PyPI.

and often newbies assume that the
strip/lstrip/rstrip family behaves like this, but of course they don't.

I get tired of writing stuff like:

if path.startswith('html/'):
path = path[len('html/'):]
elif s.startswith('text/'):
path = path[len('text/'):]

So create a function (example below) and put it along with others in a
module called (say) andyutils.py ...

def chop_known_prefixes(path, prefixes):
for prefix in prefixes:
if path.startswith(prefix):
return path[len(prefix):]
return path

By the way, what do you do if path doesn't start with one of the "known"
prefixes?
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

In the event that path contains (say) 'html/text/blahblah...', this
produces 'blahblah...'; the original tedious stuff produces
'text/blahblah...'.
Does anyone else find this to be a common need? Has this been suggested
before?


You can answer that last question yourself by googling comp.lang.python ...
Jul 19 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.