Method for reversing strings

I would like to add a .reverse() method for strings. I think most modern languages have something like that and [::-1] is a bit archaic with little charm. There may be other methods like splitting the string, reversing the resulting list, and then joining it back, but that’s a bit of work!

There have been several times in my QA career where I am scripting in Python and need to reverse a string, but I have to look up the [::-1] syntax because it is not intuitive.

1 Like

You have another option in ''.join(reversed('some string')) that might be a bit easier to remember.

I would bet that there have been prior discussions on this topic, it would be worth unearthing those and addressing any objections raised in them if you want to push for this.

3 Likes

Right. I figured something like that existed, but it’s still a bit yeesh in my book.

I figured this was brought up before, but I didn’t actually see it. I will look again and link it here if i can find it, and then address it/drop it as suggested.

I find [::-1] much easier to remember than a .reverse() method would be. Because it’s just an application of the python slice pattern iterable[a:b:c] rather than a whole new keyword to learn.

Are you happy with reversed, or would you be much happier if you could write it as my_string.|reversed() or something like that?

3 Likes

There’s nothing wrong with [::-1], and it’s a standard thing to see in Python. I really don’t think adding another way to do this would make things less confusing, most of the time.

9 Likes

Looks like the topics that were relevant were more asking how to do it when I searched for string reverse and [::-1]. For example:

The suggested name .reverse is misleading as it uses an imperative verb which usually indicates an in-place operation like sort vs sorted. Also list has a reverse method which does perform an in-place reversal. It should be at least called .reversed() or something rather than .reverse().

I prefer [::-1] which is more general and works for any sequence. I can’t remember when I have reversed a string but I often reverse lists, tuples, arrays etc. There is also the reversed function for when you want to iterate over a sequence in reverse order.

6 Likes

str often uses imperative verbs to indicate non-mutating operations (.center, .capitalize, .encode, .removeprefix, .removesuffix, .replace, .strip, .swapcase)

6 Likes

What is the advantage over reversed(some_string)?

The result of reversed(some_string) is not a string.

Note though my earlier statement:

If you find yourself needing to reverse strings often then I am interested to know why that is because it is not something that ever arises for me.

4 Likes

@peterc I suspect most people within this community would be of the same opinion that they would prefer [::-1]. It’s not like I do not understand that pattern and I am not saying remove the [::-1] syntax.

The reversed() function is a bit interesting. My thinking: why bother having that function when there is slicing? I am guessing because the reversed() function is meant for other things and a string simply fits that pattern. Does slicing also fit that pattern in the other contexts as well? Yes.

print(''.join(reversed(['1','2','3'])))   # 321
print(''.join(['1','2','3'][::-1]))       # 321

I suppose the point is that redundancy doesn’t appear to be a problem. The problem with the reversed() function is that, while it does reverse a string, it doesn’t return a string. So I would not call it faithful. As @oscarbenjamin pointed out, too.

@oscarbenjamin I understand your point on naming and suspect there would be some opinions on implementation/naming, so .reverse() or .reversed() is up in the air for me. I do find it interesting that an imperative verb would be misleading and imply anything about implementation. (At risk of going on.a big tangent ->) Can you elaborate on the nuance? From what I understand, telling the language to do something means I expect that thing to be done. I do not care what happens behind the scenes. If I do care about the how, then I would think I would need to dictate that as well.

As for your later question into why I use it, the most recent and common example for me: I make test data a bit more unique but still trace-able. When I write scripts, I generally like to include the user story or test case number into the data somewhere like maybe a name of test1234. If I need to input another but need to keep them separated, I can do 4321tset because that will also catch my eye and I know where it came from. Like anything else, there are other ways to do it. For example, I could do test1234a, test1234b, and so on, but depending on the length of the number and field limitations that might not be possible. The other side of your question might be how often do you use some of the other string methods? I can tell you I have never used isprintable() :smiley:

Edit: sorry for the novel :frowning:

reversed() is not a pair to slicing, it’s a pair to iter(). It gives you a reverse iterator.

“Reversing string” is not well defined problem. Just reversing the order of characters is not always the right solution. It does not work with combinig characters, the RTL mark, many emojies. “\r\n” is a line separator often used in internet protocols, but “\n\r” is not. What does it mean – reversing a multiline string? Should it revers every line separately? Should it reverse the order of lines? Should reversion transform “<” to “>”, “«” to “»”, “p” to “q”?

There are no simple answers to these questions, and there can not be universal solution. You need different solutions depending on the context, and this cannot be implemented as a string method.

If you consider string as a sequence of characters, then use [::-1] – this works for all sequences. If you consider string as an iterable of characters, then use reversed() – this works for all reversible iterables. In other cases, use other methods provided bu third-party libraries (like ICU) or implement your own algorithm that fits your needs.

17 Likes

Obviously yes! Because it is a source of nightmares that ()() is not a palindrome but ())( is. This is unacceptable and should be fixed!!!

5 Likes

I don’t think I’ve ever had to reverse a string. A list of values, yes, but not a plain string.

We already have a way to write this ('abcd'[::-1]) and don’t need a second one, IMO.

Slicing is a fairly general language feature. I think I learned it from the official Python tutorial where it was introduced early on before even functions. I use slicing with strings quite often to get e.g. the first or last part of a string. I also use slicing with lists, tuples, arrays etc. While I may never have reversed a string in those other situations using slicing to reverse the order is not uncommon. The fact that strings support slicing the same as all those other types mean that I know immediately how to do it even if I have possibly never actually wanted to reverse a string before.

2 Likes

Sometimes an array.array of Uniocde chars (typecode ‘w’) is more convenient for this sort of string modifications (character reordering, replacing, etc.).

@storchaka I think those questions are pretty easy to answer! Just ask yourself, what would [::-1] do? :smiley:
image

I think you’re on one hand conflating the meaning of the strings with the characters ( for example, not this ><) and on the other hand focusing solely on the characters to make your point. I understand your point, and I am not looking to solve everything with a golden hammer. On the other hand, I can’t help but have this come into my mind:
image

Which is a decent enough transition to @oscarbenjamin ! Hey, I get you- you like slicing and you are not alone! @malemburg is right there with you. What did you want to do with the first/last part of the string and could you use a string specific built in? I am hinting at some of the built-ins that exist are basically just syntactic sugar for slicing:


The docs are another issue. I think the [] syntax is basically a footer on the slice() function.

Serhiy has a broader point, though, because “reversing” something doesn’t have a single meaning. Allow me to draw an example from Team Fortress 2. One of the unlockable items is a chocolate bar, and since it’s used by the Russian character, its name is taken from the Russian word for chocolate: “шоколад”, which transliterates as “shokolad”. The name is then reversed. Since the “sh” is actually a single letter, reversing the Russian word before transliterating would name the bar “dalokosh”; but reversing the Latinized version gives “dalokohs”.

Valve chose the second option but I would say that the first option is at least as valid.

The same problem could happen in other ways. Let’s suppose that I use the ISO 9 transliteration instead: “dalokoš”. This adorns the “s” with a caron instead of using the additional letter ‘h’. That’s still a single letter, right? Well, maybe. Is it "\u0161" or is it "s\u030c"? If it’s the latter, then reversing the string codepoint-by-codepoint would swap the order of the base character and the combining character, which makes no linguistic sense.

Fortunately Python doesn’t [1] have to worry about code units getting out of order. If the emoji “RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS” is stored in UTF-16, it becomes the two code units “\ud83d\udd96” which can themselves be put out of order. But that won’t happen in Python’s strings, since the entire “\U0001F596” is a single character and would not be reversed.

Reversing things is hard :slight_smile:


  1. since Python 2.7’s demise, at least ↩︎

8 Likes

These aren’t really there because of slicing, though. They exist to be a pair to lstrip/rstrip, which are very frequently misunderstood; and to match the effect with slicing, you have to repeat the affix or have a magic number for its length. See the original PEP for some examples:

So while they CAN be done with slicing, it’s unideal. I wouldn’t call it just syntactic sugar.

2 Likes