Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A weird string literal identity issue in Django

Tags:

python

django

I have an API in the Django project I am working on that sends commands to devices. The API expects a POST request with data containing something like {"command": "activate"}. A few minutes ago I found this bit of code in the view function for the API

... ommitting the DRF viewset class def for brevity ...
def command(self, request):
    if 'command' in request.data and request.data['command'] is not 'activate':
        ... do things that we need to do to send the activate command...

I figured that someone (most likely myself) made a logic error and fixed it to request.data['command'] is 'activate', but immediately realized that the API actually works as is. That is this if statement evaluates as True and commands do get sent even though it clearly states request.data['command'] is not 'activate'

So I started debugging and eventually found out that request.data['command'] != 'activate' returns False as expected and breaks the code, but request.data['command'] is not 'activate' returns True. As far as I can tell, the difference between is not and != is that is not compares identity where != compares value. But, again, as far as I know, literals should have the same identity no matter where they come from. A quick test in ipython seems to confirm this

 In [1] x = {'command': 'activate'}
 In [2] x['command'] is 'activate'
 Out[2] True
 In [3] x['command'] is not 'activate'
 Out[3] False

What the hell is going on? Why doesn't it work in the view?

like image 733
Mad Wombat Avatar asked Dec 28 '25 09:12

Mad Wombat


1 Answers

Do not rely on string comparison by identity, in any cases. The fact that it appears to work sometimes is due to an implementation detail of CPython called string interning. The rules governing whether a given string will be interned or not are very complicated, and subject to change without notice.

For example, with a slight modification of your original example we can change the behaviour:

>>> x = {'command': 'activate.'}
>>> x['command'] is 'activate.'
False

Use == and != for string comparisons.

like image 85
wim Avatar answered Dec 30 '25 23:12

wim