Should you use "not not x" instead of "bool(x)" in Python? (NO!)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome i'm james murphy and in this episode we're going to be talking about a curious idiom not not x and how using not not x actually turns out to be faster than using the built-in bool operator on x so i actually had a viewer comment and ask james could you cover why this not not x thing is faster than boolex and when i first read this comment i thought to myself there's no way right i mean they're computing the same thing not not x is just going to call the you know boolean conversion operator for x just like bool does how could one be any faster or slower than the other but i thought about it for a little while and then i decided i would at least test it and so i wrote up a little script to test it and here's what i found quite surprisingly bull took 1.08 seconds for 10 million trials versus only 0.68 seconds for not not x you can see the testing code that i used here just have 10 million trials and then i use the built-in time it dot time it in order to time each of the functions so since i'm using 10 million trials that's why these are in seconds and not in tenths of a microsecond no matter how many times i run it i've tried changing the order of the tests all of the different ways that i've tried it bool x always takes much longer than not not x so how can that be and why the first thing to note is that bool like any other name is a variable and you have to look up that variable in this case in the global scope so this function involves looking up the name bool whereas this function doesn't have to look anything up it's always going to call the boolean conversion operator because there's no way to override not like there is a way to override bool for instance i could just say bool equals stir and you know that changes the value that bool is here now bool is pointing to the built-in stir instead of to the built-in bool so because this value of bool can change it has to be looked up at runtime so i went ahead and added this y equals bool line into this conversion function it doesn't actually do anything as far as the return value is concerned it's just trying to put it on a level playing field so in this case this function now also has to look up the name bool but as we see from the runtimes not not x is still faster so it's not the lookup of the name bool which is causing the slowdown it's something else for this test i'm doing something very similar which is i'm still looking up the name bool but in this case i'm also actually calling the bull function i'm just default constructing a boolean so that's going to give me true and i'm storing it and then not doing anything with it and still just returning not not x but in this case we can see that the times are very very closely aligned just 0.93 and 0.94 what this is telling me is essentially that the slowdown is caused by how slow it is to actually call a function in python not not x doesn't actually go through the mechanism of calling a function in python it actually has a special fast path programmed in which allows it to execute just directly going to the boolean conversion rather than calling the bool function which then does the conversion that's why adding the unnecessary name lookup and function call basically makes the times equal again we can confirm our intuition by plugging the functions into compiler explorer and looking at the output so this top four set of instructions is the code for the bool convert function once it's been compiled into bytecode as you can see there's the load global or the bool operator so it has to load bool and then it loads the local variable x and then calls the function which is the bool that was just loaded and then returns whereas in the not not conversion there's a special instruction called unary not which gets called twice instead of calling any function just for completeness here's what you see when you throw back in the redundant read so in this case this instruction you can see when i hover my mouse over it it highlights the three instructions on the right here that instruction corresponds to loading the global pool then calling that function and storing it in the variable y in particular you can see that even though the variable y isn't used later in the function and it could theoretically just be removed and optimized away those kinds of optimizations generally don't happen when you compile things into python byte code there are some situations where things get compiled ahead of time like if you add two constant integers together but most of the time you will actually see redundant operations even if there's basically no chance that they could ever be used in the actual code so that's pretty cool that we can explain what exactly is the difference between knot knot and bullex and this is how we're making up the difference in time but for now let's just get rid of this redundant read and compare to a few other things so down here i also have how long does it take to call the double under or dunderbool function of the variable and also i'm comparing to just a no op looking at the times of course we find that the no op just a function call that passes does nothing is the fastest followed by the not not and then the method call of the dunderbool and then the calling of the global bull take about the same amount of time now i did run this a few times and it seemed like most of the time actually the method call was even slower but just by a little bit i think that most of the time though you don't really care about converting an x to a bool what you really want is to just use it in an if statement so let's also compare against uh doing just an if x versus an if not not x thankfully we see that in this case both if x and if not not x are on par with just a little bit slower than a no op function call so if x and if not not x are not calling the bool function as a function and then doing stuff it definitely has a fast path which is converting the x to a pool in a different way and i actually have a whole video on that if you want to check it out additionally the times for if x and if not not x are very close so i wonder if they're doing anything different interestingly when we plug the example into compiler explorer we see that an optimization is actually happening here if x is being converted into these four instructions we see load and then pop jump if false and then a load and return if not not x you can compare line for line is doing the exact same thing that means that there is literally no difference in the byte code between if x and if not nonex so please don't write not not x that's just going to confuse people just write if x so speaking of not confusing people suppose you're in a situation where you do actually want to call bool x you're not just going to use x in an if statement immediately later you actually want to store the value of the boolean conversion in that case is it worth it to use not not x and potentially confuse your readers instead of just calling bool x i'm going to go ahead and say no you should always just be using bool x although the difference in timing between not not x and bool x is going to be significant in the statistical sense it's definitely not going to be significant in the real world sense remember this was for 10 million iterations so we still have a pretty small difference in 10 million iterations so unless you're counting tenths of a microsecond then you really shouldn't be caring about whether you use not not x or boulex so just use the one that's more readable and if you are counting tenths of a millisecond then you probably shouldn't be doing whatever you're doing in python well just because the video told you to keep doing what you were probably already doing doesn't mean it wasn't interesting right in any case thank you as always to my patrons and donors if you especially like my video please consider becoming a subscriber or becoming a patron or donor i really appreciate the support don't forget to slap that like button an odd number of times of course and i'll see you next time thanks for watching
Info
Channel: mCoding
Views: 105,453
Rating: undefined out of 5
Keywords: python
Id: 9gEX7jesV34
Channel Id: undefined
Length: 8min 9sec (489 seconds)
Published: Sat Aug 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.