Source

Python mind-teaser: Make the function return True

Hi everyone! 👋 I was browsing /r/python and came across this post:

Image

The challenge was easy. Provide such an input that if 1 is added to it, it is the instance of the same object but if 2 is added it is not.

Solution 1: Custom class

The way I personally thought to solve this challenge was this:

def check(x):
    if x+1 is 1+x:
        return False
    if x+2 is not 2+x:
        return False
    return True

class Test(int):
    def __add__(self, v):
        if v == 1:
            return 0
        else:
            return v

print(check(Test()))
# output: True

Let me explain how this works. In Python when we use the + operator Python calls a different dunder method depending on which side of the operator our object is. If our object is on the left side of the operator then __add__ will be called, if it is on the right side then __radd__ will be called.

Our Test object will return 0 if Test() + 1 is called and 1 if 1 + Test() is called. The trick is that we are overloading only one dunder method and keeping the other one same. This will help us pass the first if condition. If you take a second look at it you will see that it helps us pass the second if check as well because we simply return the input if it is not 1 so Test() + 2 will always be similar to 2 + Test().

However, after reading the comments, I found another solution which did not require a custom class.

Solution 2: A unique integer

User /u/SethGecko11 came up with this absurdly short answer:

def check(x):
    if x+1 is 1+x:
        return False
    if x+2 is not 2+x:
        return False
    return True

print(check(-7))
# output: True

Only -7 works. Any other number will not return True. If you are confused as to why this works then you aren’t alone. I had to read the comments to figure out the reasoning.

So apparently, in Python, integers from -5 to 256 are pre-allocated. When you do any operation and the result falls within that range, you get the pre-allocated object. These are singletons so the is operator returns True. However, if you try using integers which don’t fall in this range, you get a new instance.

The memory requirement for pre-allocating these integers is not that high but apparently the performance gains are huge.

So when you use -7 as input, you get a new instance of -6 but the same instance when the answer is -5. This doesn’t work with the upper bound (256) precisely because of the way if statements are constructed. 255 would work as an answer if the check function was implemented like this:

def check(x):
    if x+1 is not 1+x:
        return False
    if x+2 is 2+x:
        return False
    return True

I hope you learned something new in this article. I don’t think you would ever have to use this in any code-base ever but it is a really good mind-teaser which can catch even seasoned Python developers off-guard.

Happy programming! I will see you in the next article 😊

Newsletter

×

If you liked what you read then I am sure you will enjoy a newsletter of the content I create. I send it out every other month. It contains new stuff that I make, links I find interesting on the web, and occasional discount coupons for my book. Join the 5000+ other people who receive my newsletter:

I send out the newsletter once every other month. No spam, I promise + you can unsubscribe at anytime

✍️ Comments

Andrzej Klajnert (@aklajnert)

The second solution is brilliant! I love it :)

Yasoob
In reply to Andrzej Klajnert (@aklajnert)

Right?! I just learned something about the Python internals which I perviously didn’t know.

Antonio Frontino

On the same post on Reddit, my solution was 0.5 and every float number.

Yasoob
In reply to Antonio Frontino

Hmmm. That didn’t work for me.

>>> check(0.5) False

Do you mind sharing why 0.5 should work?

Justin T Washington

Hi Yasoob! This was a super cool post. I love learning about the intricacies of Python. Quick question: Some of the comments on the reddit post said that the range of preallocated ints an implementation detail of the interpreter CPython, rather than a feature of Python itself. I’m wondering what strategy you use to partition elements of the language, the interpreter, and other parts I might not be familiar with – or if it’s colloquially acceptable to just bundle them all together. I’d love to hear your thoughts on this.

Say something

Send me an email when someone comments on this post.

Thank you!

Your comment has been submitted and will be published once it has been approved. 😊

OK