Anti-Python

Aug 29, 2024

4 mins read

A few days ago I came across a post on LinkedIn that discussed an interesting Python interview question. The task was to reverse a string. When a candidate produced an idiomatic solution my_string[::-1] the interviewer asked to try again without using conventional string slicing. At this stage the puzzled candidate began to question the sanity of the situation and sank into some sort of an existential crisis.

As ridiculous as the task is let’s see if we can come with a better solution to this problem.

Problem

Here is the formulation of the problem:

In Python reverse the string without using the slicing operator

Problem Analysis

We need to produce a copy of the string in which all the characters are reversed. Since we are specifically prohibited from using the idiomatic and pythonic solution we need to come up with a different way. It is implied that the solution should be unidiomatic, un-pythonic, hard to comprehend and slow. What is the first thing that comes to mind? Oneliners!

Solution

And here is the answer:

result = (lambda s, z=dict(): (z.update({0: list(s)}), z[0].reverse(), "".join(z[0]))[-1])("12345")

We could have done better in terms of further pessimising the performance but I will leave it as an exercise to the reader.

To understand how we ended up with this ugly mess let us first implement it as a function:

def backwards(s):
    z = list(s)
    z.reverse()
    return "".join(z)

We converted string s into a list of characters, reversed the list and turned it back into a string using the join method of the string class. A standard way to turn a function into a one-liner is to use a lambda. And here comes the problem: assignment operators are not allowed in a lambda function in Python. A common trick to overcome this limitation is to use a second parameter as temporary storage. We can use a dictionary for that purpose:

def backwards(s, z=dict()):
    z.update({0: list(s)})
    z[0].reverse()
    return "".join(z)

We are not allowed to use assignments in a lambda but a call to a member function is fine. And now we have to deal with a second obstacle: multiple statements are not allowed inside a lambda function. As a work-around we can (ab)use a tuple. This code:

(z.update({0: list(s)}), z[0].reverse(), "".join(z))

will produce (None, None, "54321") since the update method of the dict returns None as well as the reverse method on the list stored in z[0]. The order of operations while constructing a tuple is guaranteed in Python so we can be certain that the update method is called first, followed by the reverse and join. All we have to do now is to return the last element of the tuple from the lambda function.

There is one caveat, though. If you run this code through a Python linter it will issue the following warning: “Default argument value is mutable”. This means that the backwards function has a nasty side-effect. It alters the default value of z. Indeed if we try this code:

def b(s, z=list()):
    z.extend(list(s))
    z.reverse()
    return "".join(z)

print(b("123"))
print(b("abc"))

we will get

321
cba123

Our implementation uses a dictionary as temporary storage to mitigate this issue.

Finally, to spice up the solution we use a common Javascript trick called Immediately Invoked Function Expression (IIFE). In Python it looks like this:

(lambda x: x+1)(2)

Conclusion

When I first read the LinkedIn post I really sympathised with the candidate who was given a seemingly ridiculous task. Was his surprise and protest a natural reaction given the usual stress of the interview? I believe so. As someone who has been on both sides of the process I try to reduce the level of stress as much as possible and I do not approve a practice of giving such tasks.

However, interviewers can feel as much stress as candidates and sometimes make mistakes and ask questions that they could later regret. A good strategy to deal with a situation like that would be to start a discussion and work out the solution together. As silly as my code might be I believe it serves the purpose of overcoming an awkward situation and dissolving the tension. As a bonus you demonstrated a few advanced Python techniques.

Good luck with your next interview!