-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PEP 616: Add str.removeprefix and str.removesuffix methods #84120
Comments
Following discussion here ( https://mail.python.org/archives/list/python-ideas@python.org/thread/RJARZSUKCXRJIP42Z2YBBAEN5XA7KEC3/ ), there is a proposal to add new methods str.cutprefix and str.cutsuffix to alleviate the common misuse of str.lstrip and str.rstrip. I think sticking with the most basic possible behavior def cutprefix(self: str, prefix: str) -> str:
if self.startswith(prefix):
return self[len(prefix):]
# return a copy to work for bytearrays
return self[:]
def cutsuffix(self: str, suffix: str) -> str:
if self.startswith(suffix):
# handles the "[:-0]" issue
return self[:len(self)-len(suffix)]
return self[:] would be best (refusing to guess in the face of ambiguous multiple arguments). Someone can do, e.g. >>> 'foo.tar.gz'.cutsuffix('.gz').cutsuffix('.tar')
'foo' to cut off multiple suffixes. More complicated behavior for multiple arguments could be added later, but it would be easy to make a mistake in prematurely generalizing right now. In bikeshedding method names, I think that avoiding the word "strip" would be nice so users can have a consistent feeling that "'strip' means character sets; 'cut' means substrings". |
To be clear, are you only making a copy of the unchanged object if it is a mutable bytearray, not str or bytes? |
Yes: >>> x = "A"*10**6
>>> x.cutprefix("B") is x
True
>>> x.cutprefix("") is x
True
>>> y = b"A"*10**6
>>> y.cutprefix(b"B") is y
True
>>> y.cutprefix(b"") is y
True
>>> z = bytearray(b"A")*10**6
>>> z.cutprefix(b"B") is z
False
>>> z.cutprefix(b"") is z
False I'm not sure whether this should be part of the spec or an implementation detail. The (str/bytes).replace method docs don't clarify this, but they have the same behavior: >>> x = "A"*10**6
>>> x.replace("B", "C") is x
True
>>> x.replace("", "") is x
True
>>> y = b"A"*10**6
>>> y.replace(b"B", b"C") is y
True
>>> y.replace(b"", b"") is y
True
>>> z = bytearray(b"A")*10**6
>>> z.replace(b"B", b"C") is z
False
>>> z.replace(b"", b"") is z
False |
Guido, do you support this API expansion? |
I stopped following the discussion at some point, but I think this is worth adding it -- I have seen this done over and over again, and apparently lots of other people have felt the need too. I think these names are fine, and about the best we can do (keeping in line with the "feel" of the rest of the string API). I like the behavior of returning a copy of the string if there's no match (as opposed to failing, which was also brought up). If the original object is immutable this should return the original object, but that should be considered a CPython optimization (IIRC all the string methods are pretty careful about that), but not required by the spec. FWIW the pseudo code has a copy/paste error: In cutsuffix() it should use endswith() rather than startswith(). |
The proposed change will affect many builtin types: bytes, bytearray, str, but also other types like collections.UserString. Would it make sense to summarize what has been said in the python-ideas thread into a PEP? It may good to specify things like: >>> x = "A"*10**6
>>> x.cutprefix("B") is x
True The specification can be just "that's an implementation detail" or "CPython implementation specific" :-) I don't expect such PEP to be long nor controversial, but it may help to write it down. |
If no one has started, I can draft such a PEP. |
Sounds good. |
Here is a draft PEP -- I believe it needs a Core Developer sponsor now? |
The PEP is a good start. Can you try to convert it to a PR on https://github.com/python/peps/ ? It seems like the next available PEP number is 616. I would prefer to leave comments on a PR. |
IMHO the names don't fit Pythons current naming scheme, so what about naming them "lchop" and "rchop"? |
Thank you. And good luck for handling incoming discussions on the PEP ;-) |
Where should I leave comments on the PEP? Do you plan to post it on python-dev soon? |
Just posted it. |
Dennis Sweeney wrote https://www.python.org/dev/peps/pep-0616/ |
The documentation should explain well the difference between removeprefix()/removesuffix() and lstrip()/strip()/rstrip(), since it is the rationale of the PEP ;-) An example that can be used to explain the difference: >>> "Monty Python".removesuffix(" Python")
'Monty'
>>> "Monty Python".strip(" Python")
'M' |
When, I even expect that some people use .strip() whereas their intent was to use .lstrip(): >>> "Python vs Monty Python".strip("Python")
' vs Monty ' Again, strip() is used with a string whereas the real intent was to use removesuffix() which didn't exist ;-) A note should be added to lstrip(), strip() and rstrip() documentation to point to removeprefix() and/or removesuffix(). |
Please add an underscore to the names: remove_prefix(). and remove_suffix(). The latter method causes a mental hiccup when first reading as removes-uffix, forcing mental backtracking to get to remove-suffix. We had a similar problem with addinfourl initially being read as add-in-four-l before mentally backtracking to add-info-url. |
The PEP-616 was approved with removeprefix() and removesuffix() names. The rationale for the names can be even found in the PEP: |
I disagree with the rationale given in the PEP. The reason that "startswith" and "endswith" don't have underscores is that the aren't needed to disambiguate the text. Our rules are to add underscores when it improves readability, which in this case it does. Like casing conventions, these rules became prevent after the early modules were created (i.e. the older the module, the more likely that it doesn't follow modern conventions). We only have one chance to get this right. Take it from someone with experience with this particular problem. I created imap() but later regretted the naming pattern when if came to ifilter() and islice() which sometimes cause mental hiccups initially being read as if-ilter and is-lice. |
I'm personally -0 for underscores -- they might slightly improve readability of the function name in isolation but may also add confusion about which methods have underscores. Only one out of the 45 non-dunder str methods has an underscore right now: >>> meths = [x for x in dir(str) if not x.startswith('__')]
>>> [x for x in meths if '_' in x]
['format_map']
>>> [x for x in meths if '_' not in x]
['capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] Maybe I'm wrong, but it seemed to me that most of the discussions to date had arrived at leaving out underscores. Is there a process or appropriate channel to continue this discussion now that the PEP is accepted? |
Oops -- I now see the message on Python-Dev. |
Well done Dennis Sweeney! You got a PEP approved and now the implementation is merged! Maybe the documentation will need more reviews, but that can be done later. I prefer to get the implementation merged as soon as possible (it will likely be part of the next 3.9.0a6), so more users can play with it before 3.9.0 final release. |
There's a failure here:
Traceback (most recent call last):
...
OSError: [Errno 9] Bad file descriptor This should be unrelated to the patch, right? |
It's unrelated. It smells like bpo-39995. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: