234

I am trying to do the following, in a clear pythonic way:

def remove_prefix(str, prefix):
    return str.lstrip(prefix)

print(remove_prefix('template.extensions', 'template.'))

This gives:

xtensions

Which is not what I was expecting (extensions). Obviously (stupid me), because I have used lstrip wrongly: lstrip will remove all characters which appear in the passed chars string, not considering that string as a real string, but as "a set of characters to remove from the beginning of the string".

Is there a standard way to remove a substring from the beginning of a string?

Elazar
  • 20,415
  • 4
  • 46
  • 67
blueFast
  • 41,341
  • 63
  • 198
  • 344
  • 64
    On Python 3.9+, use [`str.removeprefix`](https://docs.python.org/3.9/library/stdtypes.html#str.removeprefix) – Boris Verkhovskiy Apr 27 '20 at 15:48
  • 6
    Useful [features for `removeprefix()` and `removesuffix()`](https://stackoverflow.com/questions/599953/how-to-remove-the-left-part-of-a-string/64400605#64400605) in the newly released Python 3.9. – Milovan Tomašević Oct 17 '20 at 09:55
  • One-liner from @martineau's answer: `remove_prefix = lambda text, prefix: text[len(prefix):] if text.startswith(prefix) else text` – Basj Apr 29 '22 at 16:47

6 Answers6

396

As noted by @Boris-Verkhovskiy and @Stefan, on Python 3.9+ you can use

text.removeprefix(prefix)

In older versions you can use with the same behavior:

def remove_prefix(text, prefix):
    if text.startswith(prefix):
        return text[len(prefix):]
    return text  # or whatever
Oliver Gondža
  • 3,386
  • 4
  • 29
  • 49
Elazar
  • 20,415
  • 4
  • 46
  • 67
  • 58
    Starting in `Python 3.9`, you can use [`removeprefix`](https://docs.python.org/3.9/library/stdtypes.html?highlight=removeprefix#str.removeprefix). For an example see [this answer](https://stackoverflow.com/a/61432546/1518225). – Stefan May 27 '20 at 18:53
  • Cool! Note: the behavior of the new function is exactly as in this answer (returning the string unchanged if it does not start with `prefix`) – Elazar May 27 '20 at 23:50
  • This is perfect for trimming `hh:mm:ss` leading zeros without harming significant zeroes. I replaced leading `'0:'` and then `'0'` to null. This allow `10:` to remain intact along with `5:08`. – WinEunuuchs2Unix Oct 18 '20 at 02:19
  • 2
    `removeprefix` — better late than never. – martineau Feb 24 '21 at 21:40
  • 3
    And the `endswith()` parallel is `removesuffix()` – Josiah Yoder Jul 29 '22 at 19:12
  • a side note is that [pep-0616](https://peps.python.org/pep-0616/) that added the method to 3.9, includes example polyfill code and their behavior is to return copy of the string when no match is made: `return self[:]` – glen Jan 09 '23 at 15:32
  • @glen this does not guarantee copying (which would be useless anyway). `x = 'a b'; assert x is x[:]` works in CPython3.11 – Elazar Jan 10 '23 at 00:18
74

Short and sweet:

def remove_prefix(text, prefix):
    return text[text.startswith(prefix) and len(prefix):]
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 7
    @jamylak it's like the "ternary" and-or, but correct :) ... +1, although I think it's a bit too clever for real code. And you can't decide you want it to raise an exception otherwise. – Elazar Jun 03 '13 at 09:15
  • @Oliver: I disagree…it's just idiomatic Python coding for those that know how the language works. – martineau Jul 27 '20 at 15:54
  • 11
    Well you definitely have to think a lot longer when reviewing code like this in comparison to the accepted answer, even if you work with Python more often. – Thomas Aug 13 '20 at 14:37
  • 7
    Downvoted. This is the kind of unnecessary clever stuff that makes reading even MY OWN CODE more difficult. – MrR Dec 20 '20 at 02:18
  • 1
    @MrR: Python also has another, frequently underused, feature: comments. – martineau Dec 20 '20 at 05:33
  • 1
    @martineau So you're proposing writing something cleverly, then comment on it to understand it better, rather than just writing in a simpler straighforward-to-understand-without-comment fashion. No, thanks. – MrR Dec 24 '20 at 02:47
  • @MrR: I only suggested doing that for those who are unable to recognize what it's doing (likely because they don't understand the language or its idioms well). Another nice feature of comments is they don't slow down code execution. – martineau Dec 24 '20 at 03:37
  • 4
    Comments risk becoming detached from the code they refer to, so are a far cry from a panacea for code clarification. I much prefer code that's self-commenting. – MrR Dec 27 '20 at 04:43
  • 1
    I think it is not only clever but also readable: One should know that boolean are underlying integers. If you want to make clearer that we are using the boolean of "startswith" for calculation I suggest to replace the `and` with a `*`. This might - in fact - be a bit slower, but gives a - very strong - indicator to what we are doing here. – Make42 Feb 24 '21 at 16:32
  • @Make42: Good point about using `*` which doesn't require understanding how the `and` operator works — even though `*` itself has a number of different meanings in Python, depending on context. My personal goal is to write code that's both efficient and understandable by anyone who knows the language at least as well as I do. `;¬)` – martineau Feb 24 '21 at 17:17
  • "as well as I do" - however consider that you yourself do not know the a specific aspect of the language continuously well. Meaning: You learn knew stuff and forgot other stuff. Also: You do not code for the machine, you do not code for yourself, you code for other programmers. (Even if that other programmer is you, but at a different time.) So, in a way the remarks are somewhat valid. Although I feel that your code is (barely) not too clever. – Make42 Feb 24 '21 at 20:25
  • @Make42: I generally don't write code for me or anyone else who even learned the basics of the language yet such as how the boolean operators work. Yes, there was a short period where I might not have known it well, but again, that's not a target audience I any concerns about at all. Anyway, as I said in my very first comment far above, I don't consider the code "too" (or even "very") clever either—so we're basically in agreement. – martineau Feb 24 '21 at 22:52
54

What about this (a bit late):

def remove_prefix(s, prefix):
    return s[len(prefix):] if s.startswith(prefix) else s
mshsayem
  • 17,557
  • 11
  • 61
  • 69
17

regex solution (The best way is the solution by @Elazar this is just for fun)

import re
def remove_prefix(text, prefix):
    return re.sub(r'^{0}'.format(re.escape(prefix)), '', text)

>>> print remove_prefix('template.extensions', 'template.')
extensions
jamylak
  • 128,818
  • 30
  • 231
  • 230
16

I think you can use methods of the str type to do this. There's no need for regular expressions:

def remove_prefix(text, prefix):
    if text.startswith(prefix): # only modify the text if it starts with the prefix
         text = text.replace(prefix, "", 1) # remove one instance of prefix
    return text
Blckknght
  • 100,903
  • 11
  • 120
  • 169
4
def remove_prefix(s, prefix):
    if s.startswith(prefix):
        return s[len(prefix):]
    else:
        return s
Suraj
  • 2,253
  • 3
  • 17
  • 48
Zacrath
  • 521
  • 3
  • 8