2017-08-30 23:07:27 +03:00
< h1 align = " center " > What the f * ck Python ? 🐍 < / h1 >
2017-08-31 16:24:16 +03:00
< p align = " center " > A collection of interesting and tricky Python examples . < / p >
2017-08-26 21:31:11 +03:00
[ ! [ WTFPL 2.0 ] [ license - image ] ] [ license - url ]
2017-08-31 14:31:41 +03:00
Python , being awesome by design high - level and interpreter - based programming language , provides us with many features for the programmer ' s comfort. But sometimes, the outcomes of a Python snippet may not seem obvious to a regular user at first sight.
2017-08-28 22:22:34 +03:00
2017-08-31 14:31:41 +03:00
Here is a fun project attempting to collect such classic and tricky examples of unexpected behaviors in Python and discuss what exactly is happening under the hood !
2017-08-29 22:10:21 +03:00
2017-08-31 14:31:41 +03:00
While some of the examples you see below may not be WTFs in the truest sense , but they ' ll reveal some of the interesting parts of Python that you might be unaware of. I find it a nice way to learn the internals of a programming language, and I think you ' ll find them interesting as well !
2017-08-29 22:10:21 +03:00
2017-08-30 21:29:05 +03:00
If you ' re an experienced Python programmer, you might be familiar with most of these examples, and I might be able to revive some sweet old memories of yours being bitten by these gotchas :sweat_smile:
2017-08-26 21:31:11 +03:00
2017-08-30 19:37:46 +03:00
So , here ya go . . .
2017-08-30 19:24:35 +03:00
2017-08-26 21:31:11 +03:00
# Table of Contents
< ! - - START doctoc generated TOC please keep comment here to allow auto update - - >
< ! - - DON ' T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
2017-08-30 14:21:15 +03:00
- [ Structure of the Examples ] ( #structure-of-the-examples)
2017-08-30 20:27:10 +03:00
- [ Usage ] ( #usage)
- [ 👀 Examples ] ( #-examples)
2017-08-30 22:06:23 +03:00
- [ Skipping lines ? ] ( #skipping-lines)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation)
2017-08-30 22:06:23 +03:00
- [ Well , something is fishy . . . ] ( #well-something-is-fishy)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-1)
2017-08-30 22:06:23 +03:00
- [ Time for some hash brownies ! ] ( #time-for-some-hash-brownies)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-2)
2017-08-31 21:55:34 +03:00
- [ Evaluation time discrepancy ] ( #evaluation-time-discrepancy)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-3)
2017-08-30 22:06:23 +03:00
- [ Modifying a dictionary while iterating over it ] ( #modifying-a-dictionary-while-iterating-over-it)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation)
2017-09-02 22:57:23 +03:00
- [ Deleting a list item while iterating over it ] ( #deleting-a-list-item-while-iterating-over-it)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-1)
2017-08-30 22:06:23 +03:00
- [ Backslashes at the end of string ] ( #backslashes-at-the-end-of-string)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-4)
2017-10-11 13:25:32 +03:00
- [ String interning ] ( #string-interning)
- [ 💡 Explanation : ] ( #-explanation-2)
2017-10-11 16:05:15 +03:00
- [ Let ' s make a giant string!](#lets-make-a-giant-string)
- [ 💡 Explanation ] ( #-explanation-5)
2017-09-02 22:57:23 +03:00
- [ Yes , it exists ! ] ( #yes-it-exists)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-3)
2017-09-02 22:57:23 +03:00
- [ ` is ` is not what it is ! ] ( #is-is-not-what-it-is)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-4)
2017-09-06 19:14:59 +03:00
- [ ` is not . . . ` is not ` is ( not . . . ) ` ] ( #is-not--is-not-is-not-)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-6)
2017-08-30 22:06:23 +03:00
- [ The function inside loop sticks to the same output ] ( #the-function-inside-loop-sticks-to-the-same-output)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-7)
2017-08-30 22:06:23 +03:00
- [ Loop variables leaking out of local scope ! ] ( #loop-variables-leaking-out-of-local-scope)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-5)
2017-09-02 22:57:23 +03:00
- [ A tic - tac - toe where X wins in the first attempt ! ] ( #a-tic-tac-toe-where-x-wins-in-the-first-attempt)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-6)
2017-09-02 22:57:23 +03:00
- [ Beware of default mutable arguments ! ] ( #beware-of-default-mutable-arguments)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-7)
2017-09-02 22:57:23 +03:00
- [ Same operands , different story ! ] ( #same-operands-different-story)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-8)
2017-09-02 22:57:23 +03:00
- [ Mutating the immutable ! ] ( #mutating-the-immutable)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-9)
2017-09-02 22:57:23 +03:00
- [ Using a variable not defined in scope ] ( #using-a-variable-not-defined-in-scope)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-10)
2017-09-02 22:57:23 +03:00
- [ The disappearing variable from outer scope ] ( #the-disappearing-variable-from-outer-scope)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-11)
2017-09-02 22:57:23 +03:00
- [ Return return everywhere ! ] ( #return-return-everywhere)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-12)
2017-09-02 22:57:23 +03:00
- [ When True is actually False ] ( #when-true-is-actually-false)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-13)
2017-09-02 22:57:23 +03:00
- [ Be careful with chained operations ] ( #be-careful-with-chained-operations)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-14)
2017-08-30 20:27:10 +03:00
- [ Name resolution ignoring class scope ] ( #name-resolution-ignoring-class-scope)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-8)
2017-08-30 20:27:33 +03:00
- [ From filled to None in one instruction . . . ] ( #from-filled-to-none-in-one-instruction)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-9)
2017-08-30 20:27:10 +03:00
- [ Explicit typecast of strings ] ( #explicit-typecast-of-strings)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-15)
2017-08-30 20:27:10 +03:00
- [ Class attributes and instance attributes ] ( #class-attributes-and-instance-attributes)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-16)
2017-08-30 20:27:10 +03:00
- [ Catching the Exceptions ! ] ( #catching-the-exceptions)
2017-09-02 22:57:23 +03:00
- [ 💡 Explanation ] ( #-explanation-10)
2017-08-30 22:06:23 +03:00
- [ Midnight time doesn ' t exist?](#midnight-time-doesnt-exist)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-17)
2017-09-04 20:46:31 +03:00
- [ What ' s wrong with booleans?](#whats-wrong-with-booleans)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-18)
2017-09-01 21:28:59 +03:00
- [ Needle in a Haystack ] ( #needle-in-a-haystack)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-19)
2017-10-11 15:12:45 +03:00
- [ yielding None ] ( #yielding-none)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-20)
2017-10-11 15:12:45 +03:00
- [ The surprising comma ] ( #the-surprising-comma)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-21)
2017-10-11 15:12:45 +03:00
- [ For what ? ] ( #for-what)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-22)
2017-10-11 15:12:45 +03:00
- [ not knot ! ] ( #not-knot)
2017-10-11 13:25:32 +03:00
- [ 💡 Explanation : ] ( #-explanation-23)
2017-10-11 15:12:45 +03:00
- [ Let ' s see if you can guess this?](#lets-see-if-you-can-guess-this)
- [ 💡 Explanation : ] ( #-explanation-24)
2017-08-30 22:06:23 +03:00
- [ Minor Ones ] ( #minor-ones)
2017-08-30 14:21:15 +03:00
- [ TODO : Hell of an example ! ] ( #todo-hell-of-an-example)
2017-08-26 21:31:11 +03:00
- [ Contributing ] ( #contributing)
- [ Acknowledgements ] ( #acknowledgements)
2017-08-30 20:27:10 +03:00
- [ Some nice Links ! ] ( #some-nice-links)
2017-08-26 21:31:11 +03:00
- [ 🎓 License ] ( #-license)
< ! - - END doctoc generated TOC please keep comment here to allow auto update - - >
2017-08-30 14:20:39 +03:00
# Structure of the Examples
2017-08-26 21:31:11 +03:00
2017-09-01 19:54:47 +03:00
* * Note : * * All the examples mentioned below are tested on Python 3.5 .2 interactive interpreter , and they should work for all the Python versions unless explicitly specified in the example description .
2017-08-30 19:37:46 +03:00
All the examples are structured like below :
2017-08-29 22:10:21 +03:00
2017-09-07 11:52:34 +03:00
> ### Some fancy Title
>
> ` ` ` py
> # Setting up the code.
> # Preparation for the magic...
> ` ` `
>
> * * Output ( Python version ) : * *
> ` ` ` py
> >> > triggering_statement
> Probably unexpected output
> ` ` `
> ( Optional ) : One line describing the unexpected output .
>
>
> #### 💡 Explanation:
>
> * Brief explanation of what ' s happening and why is it happening.
> ` ` ` py
> Setting up examples for clarification ( if necessary )
> ` ` `
> * * Output : * *
> ` ` ` py
> >> > trigger # some example that makes it easy to unveil the magic
> # some justified output
> ` ` `
2017-08-30 14:20:39 +03:00
2017-08-26 21:31:11 +03:00
2017-08-30 14:20:39 +03:00
# Usage
2017-08-26 21:31:11 +03:00
2017-08-30 21:29:05 +03:00
A good way to get the most out of these examples , in my opinion , will be just to read the examples chronologically , and for every example :
2017-08-30 14:20:39 +03:00
- Carefully read the initial code for setting up the example . If you ' re an experienced Python programmer, most of the times you will successfully anticipate what ' s gonna happen next .
2017-08-30 19:37:46 +03:00
- Read the output snippets and
+ Check if the outputs are the same as you ' d expect.
+ Make sure know the exact reason behind the output being the way it is .
2017-08-31 16:24:16 +03:00
- If no , read the explanation ( and if you still don ' t understand, shout out! and create an issue [here](https://github.com/satwikkansal/wtfPython)).
2017-08-30 21:29:05 +03:00
- If yes , give a gentle pat on your back , and you may skip to the next example .
2017-08-30 19:37:46 +03:00
2017-08-31 00:58:21 +03:00
PS : You can also read these examples at the command line . First install the npm package ` wtfpython ` ,
` ` ` sh
$ npm install - g wtfpython
` ` `
Now , just run ` wtfpython ` at the command line which will open this collection in your selected ` $ PAGER ` .
#TODO: Add pypi package for reading via command line
2017-08-30 14:20:39 +03:00
# 👀 Examples
2017-08-26 21:31:11 +03:00
2017-08-30 21:56:48 +03:00
### Skipping lines?
2017-08-27 20:39:10 +03:00
2017-09-01 19:54:47 +03:00
* * Output : * *
2017-08-27 20:39:10 +03:00
` ` ` py
2017-08-30 21:56:48 +03:00
>> > value = 11
>> > valuе = 32
>> > value
11
` ` `
2017-08-27 20:39:10 +03:00
2017-08-30 21:56:48 +03:00
Wut ?
2017-08-27 20:39:10 +03:00
2017-09-01 21:37:23 +03:00
* * Note : * * The easiest way to reproduce this is to simply copy the statements from the above snippet and paste them into your file / shell .
2017-09-01 19:54:47 +03:00
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-27 20:39:10 +03:00
2017-09-11 19:05:46 +03:00
Some non - Western characters look identical to letters in the English alphabet , but are considered distinct by the interpreter .
2017-08-27 20:39:10 +03:00
2017-08-30 21:56:48 +03:00
` ` ` py
2017-09-11 19:05:46 +03:00
>> > ord ( ' е ' ) # cyrillic 'e' (Ye)
1077
>> > ord ( ' e ' ) # latin 'e', as used in English and typed using standard keyboard
101
>> > ' е ' == ' e '
False
>> > value = 42 # latin e
>> > valuе = 23 # cyrillic 'e', Python 2.x interpreter would raise a `SyntaxError` here
2017-08-31 14:31:41 +03:00
>> > value
42
2017-08-30 21:56:48 +03:00
` ` `
2017-09-11 19:05:46 +03:00
The built - in ` ord ( ) ` function returns a character ' s Unicode [code point](https://en.wikipedia.org/wiki/Code_point), and different code positions of cyrillic ' e ' and latin ' e ' justify the behavior of the above example.
2017-08-30 21:56:48 +03:00
- - -
### Well, something is fishy...
` ` ` py
def square ( x ) :
2017-08-31 14:31:41 +03:00
"""
A simple function to calculate square of a number by addition .
"""
2017-08-30 21:56:48 +03:00
sum_so_far = 0
for counter in range ( x ) :
sum_so_far = sum_so_far + x
return sum_so_far
` ` `
* * Output ( Python 2. x ) : * *
` ` ` py
2017-08-31 14:31:41 +03:00
>> > square ( 10 )
2017-08-30 21:56:48 +03:00
10
` ` `
2017-08-31 14:31:41 +03:00
Shouldn ' t that be 100?
2017-08-30 21:56:48 +03:00
* * Note : * * If you ' re not able to reproduce this, try running the file [mixed_tabs_and_spaces.py](/mixed_tabs_and_spaces.py) via the shell.
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-30 21:56:48 +03:00
* * * Don ' t mix tabs and spaces!** The character just preceding return is a " tab " , and the code is indented by multiple of " 4 spaces " elsewhere in the example.
* This is how Python handles tabs :
> First , tabs are replaced ( from left to right ) by one to eight spaces such that the total number of characters up to and including the replacement is a multiple of eight < . . . >
* So the " tab " at the last line of ` square ` function is replaced with eight spaces , and it gets into the loop .
* Python 3 is nice enough to automatically throw an error for such cases .
2017-08-31 14:31:41 +03:00
2017-08-30 21:56:48 +03:00
* * Output ( Python 3. x ) : * *
` ` ` py
TabError : inconsistent use of tabs and spaces in indentation
` ` `
- - -
### Time for some hash brownies!
2017-09-04 20:46:31 +03:00
1 \.
2017-08-30 21:56:48 +03:00
` ` ` py
some_dict = { }
some_dict [ 5.5 ] = " Ruby "
some_dict [ 5.0 ] = " JavaScript "
some_dict [ 5 ] = " Python "
2017-08-27 20:39:10 +03:00
` ` `
* * Output : * *
2017-08-30 21:56:48 +03:00
` ` ` py
>> > some_dict [ 5.5 ]
" Ruby "
>> > some_dict [ 5.0 ]
" Python "
>> > some_dict [ 5 ]
" Python "
` ` `
2017-08-31 14:31:41 +03:00
" Python " destroyed the existence of " JavaScript " ?
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-30 21:56:48 +03:00
2017-09-04 21:09:23 +03:00
* Python dictionaries check for equality and compare the hash value to determine if two keys are the same .
* Immutable objects with same value always have a same hash in Python .
2017-08-30 21:56:48 +03:00
` ` ` py
2017-09-04 21:09:23 +03:00
>> > 5 == 5.0
True
2017-08-30 21:56:48 +03:00
>> > hash ( 5 ) == hash ( 5.0 )
True
` ` `
2017-09-04 21:09:23 +03:00
* * Note : * * Objects with different values may also have same hash ( known as hash collision ) .
* When the statement ` some_dict [ 5 ] = " Python " ` is executed , the existing value " JavaScript " is overwritten with " Python " because Python recongnizes ` 5 ` and ` 5.0 ` as the same keys of the dictionary ` some_dict ` .
2017-08-30 21:56:48 +03:00
* This StackOverflow [ answer ] ( https : / / stackoverflow . com / a / 32211042 / 4354153 ) explains beautifully the rationale behind it .
- - -
2017-08-31 21:55:34 +03:00
### Evaluation time discrepancy
2017-08-30 21:56:48 +03:00
` ` ` py
array = [ 1 , 8 , 15 ]
g = ( x for x in array if array . count ( x ) > 0 )
array = [ 2 , 8 , 22 ]
` ` `
* * Output : * *
` ` ` py
>> > print ( list ( g ) )
[ 8 ]
` ` `
#### 💡 Explanation
2017-08-31 14:31:41 +03:00
- In a [ generator ] ( https : / / wiki . python . org / moin / Generators ) expression , the ` in ` clause is evaluated at declaration time , but the conditional clause is evaluated at run time .
2017-08-30 21:56:48 +03:00
- So before run time , ` array ` is re - assigned to the list ` [ 2 , 8 , 22 ] ` , and since out of ` 1 ` , ` 8 ` and ` 15 ` , only the count of ` 8 ` is greater than ` 0 ` , the generator only yields ` 8 ` .
- - -
### Modifying a dictionary while iterating over it
` ` ` py
x = { 0 : None }
for i in x :
del x [ i ]
x [ i + 1 ] = None
print ( i )
` ` `
* * Output : * *
` ` `
0
1
2
3
4
5
6
7
` ` `
2017-08-31 14:31:41 +03:00
Yes , it runs for exactly * * eight * * times and stops .
2017-08-30 21:56:48 +03:00
2017-09-02 22:57:23 +03:00
#### 💡 Explanation:
2017-08-30 21:56:48 +03:00
* Iteration over a dictionary that you edit at the same time is not supported .
* It runs eight times because that ' s the point at which the dictionary resizes to hold more keys (we have eight deletion entries, so a resize is needed). This is actually an implementation detail.
* Refer to this StackOverflow [ thread ] ( https : / / stackoverflow . com / questions / 44763802 / bug - in - python - dict ) explaining a similar example .
- - -
### Deleting a list item while iterating over it
` ` ` py
list_1 = [ 1 , 2 , 3 , 4 ]
list_2 = [ 1 , 2 , 3 , 4 ]
list_3 = [ 1 , 2 , 3 , 4 ]
list_4 = [ 1 , 2 , 3 , 4 ]
for idx , item in enumerate ( list_1 ) :
del item
for idx , item in enumerate ( list_2 ) :
list_2 . remove ( item )
for idx , item in enumerate ( list_3 [ : ] ) :
list_3 . remove ( item )
for idx , item in enumerate ( list_4 ) :
list_4 . pop ( idx )
` ` `
* * Output : * *
` ` ` py
>> > list_1
[ 1 , 2 , 3 , 4 ]
>> > list_2
[ 2 , 4 ]
>> > list_3
[ ]
>> > list_4
[ 2 , 4 ]
2017-08-27 20:39:10 +03:00
` ` `
2017-08-30 21:56:48 +03:00
2017-08-31 14:31:41 +03:00
Can you guess why the output is ` [ 2 , 4 ] ` ?
2017-08-27 20:39:10 +03:00
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 20:39:10 +03:00
2017-08-30 21:56:48 +03:00
* It ' s never a good idea to change the object you ' re iterating over . The correct way to do so is to iterate over a copy of the object instead , and ` list_3 [ : ] ` does just that .
` ` ` py
>> > some_list = [ 1 , 2 , 3 , 4 ]
>> > id ( some_list )
139798789457608
>> > id ( some_list [ : ] ) # Notice that python creates new object for sliced list.
139798779601192
` ` `
* * Difference between ` del ` , ` remove ` , and ` pop ` : * *
2017-09-04 21:35:07 +03:00
* ` del var_name ` just removes the binding of the ` var_name ` from the local or global namespace ( That ' s why the `list_1` is unaffected).
2017-08-30 21:56:48 +03:00
* ` remove ` removes the first matching value , not a specific index , raises ` ValueError ` if the value is not found .
* ` pop ` removes element at a specific index and returns it , raises ` IndexError ` if an invalid index is specified .
* * Why the output is ` [ 2 , 4 ] ` ? * *
- The list iteration is done index by index , and when we remove ` 1 ` from ` list_2 ` or ` list_4 ` , the contents of the lists are now ` [ 2 , 3 , 4 ] ` . The remaining elements are shifted down , i . e . ` 2 ` is at index 0 , and ` 3 ` is at index 1. Since the next iteration is going to look at index 1 ( which is the ` 3 ` ) , the ` 2 ` gets skipped entirely . A similar thing will happen with every alternate element in the list sequence .
2017-09-07 20:39:02 +03:00
* Refer to this StackOverflow [ thread ] ( https : / / stackoverflow . com / questions / 45946228 / what - happens - when - you - try - to - delete - a - list - element - while - iterating - over - it ) explaining the example
* See also this nice StackOverflow [ thread ] ( https : / / stackoverflow . com / questions / 45877614 / how - to - change - all - the - dictionary - keys - in - a - for - loop - with - d - items ) for a similar example related to dictionaries in Python .
2017-08-30 21:56:48 +03:00
- - -
### Backslashes at the end of string
* * Output : * *
` ` `
>> > print ( " \\ some string \\ " )
>> > print ( r " \ some string " )
>> > print ( r " \ some string \" )
2017-08-27 20:39:10 +03:00
2017-08-30 21:56:48 +03:00
File " <stdin> " , line 1
print ( r " \ some string \" )
^
SyntaxError : EOL while scanning string literal
` ` `
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-30 21:56:48 +03:00
2017-08-31 14:31:41 +03:00
- In a raw string literal , as indicated by the prefix ` r ` , the backslash doesn ' t have the special meaning.
- What the interpreter actually does , though , is simply change the behavior of backslashes , so they pass themselves and the following character through . That ' s why backslashes don ' t work at the end of a raw string .
2017-08-27 20:39:10 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-10-11 16:05:15 +03:00
### String interning
` ` ` py
>> > a = " some_string "
>> > id ( a )
140420665652016
>> > id ( " some " + " _ " + " string " ) # Notice that both the ids are same.
140420665652016
# using "+", three strings:
>> > timeit . timeit ( " s1 = s1 + s2 + s3 " , setup = " s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000 " , number = 100 )
0.25748300552368164
# using "+=", three strings:
>> > timeit . timeit ( " s1 += s2 + s3 " , setup = " s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000 " , number = 100 )
0.012188911437988281
` ` `
#### 💡 Explanation:
+ ` + = ` is faster than ` + ` for concatenating more than two strings because the first string ( example , ` s1 ` for ` s1 + = s2 + s3 ` ) is not destroyed while calculating the complete string .
+ Both the strings refer to the same object because of CPython optimization that tries to use existing immutable objects in some cases ( implementation specific ) rather than creating a new object every time . You can read more about this [ here ] ( https : / / stackoverflow . com / questions / 24245324 / about - the - changing - id - of - an - immutable - string ) .
- - -
2017-08-30 21:56:48 +03:00
### Let's make a giant string!
This is not a WTF at all , just some nice things to be aware of : )
` ` ` py
def add_string_with_plus ( iters ) :
s = " "
for i in range ( iters ) :
s + = " xyz "
assert len ( s ) == 3 * iters
2017-10-11 16:05:15 +03:00
def add_bytes_with_plus ( iters ) :
s = b " "
for i in range ( iters ) :
s + = b " xyz "
assert len ( s ) == 3 * iters
2017-08-30 21:56:48 +03:00
def add_string_with_format ( iters ) :
fs = " {} " * iters
s = fs . format ( * ( [ " xyz " ] * iters ) )
assert len ( s ) == 3 * iters
def add_string_with_join ( iters ) :
l = [ ]
for i in range ( iters ) :
l . append ( " xyz " )
s = " " . join ( l )
assert len ( s ) == 3 * iters
def convert_list_to_string ( l , iters ) :
s = " " . join ( l )
assert len ( s ) == 3 * iters
` ` `
* * Output : * *
` ` ` py
>> > timeit ( add_string_with_plus ( 10000 ) )
2017-10-11 16:05:15 +03:00
1000 loops , best of 3 : 972 µs per loop
>> > timeit ( add_bytes_with_plus ( 10000 ) )
1000 loops , best of 3 : 815 µs per loop
2017-08-30 21:56:48 +03:00
>> > timeit ( add_string_with_format ( 10000 ) )
2017-10-11 16:05:15 +03:00
1000 loops , best of 3 : 508 µs per loop
2017-08-30 21:56:48 +03:00
>> > timeit ( add_string_with_join ( 10000 ) )
2017-10-11 16:05:15 +03:00
1000 loops , best of 3 : 878 µs per loop
2017-08-30 21:56:48 +03:00
>> > l = [ " xyz " ] * 10000
>> > timeit ( convert_list_to_string ( l , 10000 ) )
2017-10-11 16:05:15 +03:00
10000 loops , best of 3 : 80 µs per loop
2017-08-30 21:56:48 +03:00
` ` `
2017-10-11 16:05:15 +03:00
Let ' s increase the number of iterations by a factor of 10.
2017-08-30 21:56:48 +03:00
` ` ` py
2017-10-11 16:05:15 +03:00
>> > timeit ( add_string_with_plus ( 100000 ) ) # Linear increase in execution time
100 loops , best of 3 : 9.75 ms per loop
>> > timeit ( add_bytes_with_plus ( 100000 ) ) # Quadratic increase
1000 loops , best of 3 : 974 ms per loop
>> > timeit ( add_string_with_format ( 100000 ) ) # Linear increase
100 loops , best of 3 : 5.25 ms per loop
>> > timeit ( add_string_with_join ( 100000 ) ) # Linear increase
100 loops , best of 3 : 9.85 ms per loop
>> > l = [ " xyz " ] * 100000
>> > timeit ( convert_list_to_string ( l , 100000 ) ) # Linear increase
1000 loops , best of 3 : 723 µs per loop
2017-08-30 21:56:48 +03:00
` ` `
2017-10-11 16:05:15 +03:00
#### 💡 Explanation
- You can read more about [ timeit ] ( https : / / docs . python . org / 3 / library / timeit . html ) from here . It is generally used to measure the execution time of snippets .
- Don ' t use `+` for generating long strings — In Python, `str` is immutable, so the left and right strings have to be copied into the new string for every pair of concatenations. If you concatenate four strings of length 10, you ' ll be copying ( 10 + 10 ) + ( ( 10 + 10 ) + 10 ) + ( ( ( 10 + 10 ) + 10 ) + 10 ) = 90 characters instead of just 40 characters . Things get quadratically worse as the number and size of the string increases ( justified with the execution times of ` add_bytes_with_plus ` function )
- Therefore , it ' s advised to use `.format.` or ` % ` syntax (however, they are slightly slower than `+` for short strings).
- Or better , if already you ' ve contents available in the form of an iterable object, then use ` ' ' .join(iterable_object)` which is much faster.
- ` add_string_with_plus ` didn ' t show a quadratic increase in execution time unlike `add_bytes_with_plus` becuase of the `+=` optimizations discussed in the previous example. Had the statement been `s = s + " x " + " y " + " z " ` instead of `s += " xyz " `, the increase would have been quadratic.
` ` ` py
def add_string_with_plus ( iters ) :
s = " "
for i in range ( iters ) :
s = s + " x " + " y " + " z "
assert len ( s ) == 3 * iters
>> > timeit ( add_string_with_plus ( 10000 ) )
100 loops , best of 3 : 9.87 ms per loop
>> > timeit ( add_string_with_plus ( 100000 ) ) # Quadratic increase in execution time
1 loops , best of 3 : 1.09 s per loop
` ` `
2017-08-30 21:56:48 +03:00
- - -
### Yes, it exists!
2017-08-31 14:31:41 +03:00
* * The ` else ` clause for loops . * * One typical example might be :
2017-08-30 21:56:48 +03:00
` ` ` py
def does_exists_num ( l , to_find ) :
for num in l :
if num == to_find :
print ( " Exists! " )
break
else :
print ( " Does not exist " )
` ` `
* * Output : * *
` ` ` py
>> > some_list = [ 1 , 2 , 3 , 4 , 5 ]
>> > does_exists_num ( some_list , 4 )
Exists !
>> > does_exists_num ( some_list , - 1 )
Does not exist
` ` `
2017-08-31 14:31:41 +03:00
* * The ` else ` clause in exception handling . * * An example ,
2017-08-30 21:56:48 +03:00
` ` ` py
try :
pass
except :
print ( " Exception occurred!!! " )
else :
print ( " Try block executed successfully... " )
` ` `
* * Output : * *
` ` ` py
Try block executed successfully . . .
` ` `
#### 💡 Explanation:
2017-08-31 14:31:41 +03:00
- The ` else ` clause after a loop is executed only when there ' s no explicit `break` after all the iterations.
2017-08-30 21:56:48 +03:00
- ` else ` clause after try block is also called " completion clause " as reaching the ` else ` clause in a ` try ` statement means that the try block actually completed successfully .
- - -
2017-08-30 20:40:42 +03:00
### `is` is not what it is!
2017-08-27 20:39:10 +03:00
2017-08-31 14:31:41 +03:00
The following is a very famous example present all over the internet .
2017-08-27 20:39:10 +03:00
` ` ` py
>> > a = 256
>> > b = 256
>> > a is b
True
>> > a = 257
>> > b = 257
>> > a is b
False
>> > a = 257 ; b = 257
>> > a is b
True
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 20:39:10 +03:00
* * The difference between ` is ` and ` == ` * *
* ` is ` operator checks if both the operands refer to the same object ( i . e . it checks if the identity of the operands matches or not ) .
* ` == ` operator compares the values of both the operands and checks if they are the same .
2017-09-01 11:13:44 +03:00
* So ` is ` is for reference equality and ` == ` is for value equality . An example to clear things up ,
` ` ` py
>> > [ ] == [ ]
True
>> > [ ] is [ ] # These are two empty lists at two different memory locations.
False
` ` `
2017-08-27 20:39:10 +03:00
* * ` 256 ` is an existing object but ` 257 ` isn ' t**
2017-08-30 21:29:05 +03:00
When you start up python the numbers from ` - 5 ` to ` 256 ` will be allocated . These numbers are used a lot , so it makes sense just to have them ready .
2017-08-27 20:39:10 +03:00
Quoting from https : / / docs . python . org / 3 / c - api / long . html
2017-08-30 21:29:05 +03:00
> The current implementation keeps an array of integer objects for all integers between - 5 and 256 , when you create an int in that range you just get back a reference to the existing object . So it should be possible to change the value of 1. I suspect the behavior of Python , in this case , is undefined . : - )
2017-08-27 20:39:10 +03:00
` ` ` py
>> > id ( 256 )
10922528
>> > a = 256
>> > b = 256
>> > id ( a )
10922528
>> > id ( b )
10922528
>> > id ( 257 )
140084850247312
>> > x = 257
>> > y = 257
>> > id ( x )
140084850247440
>> > id ( y )
140084850247344
` ` `
2017-09-01 11:13:44 +03:00
Here the interpreter isn ' t smart enough while executing `y = 257` to recognize that we ' ve already created an integer of the value ` 257 ` and so it goes on to create another object in the memory .
2017-08-27 21:47:59 +03:00
2017-08-27 20:39:10 +03:00
2017-08-30 21:29:05 +03:00
* * Both ` a ` and ` b ` refer to the same object , when initialized with same value in the same line . * *
2017-08-27 20:39:10 +03:00
` ` ` py
>> > a , b = 257 , 257
>> > id ( a )
140640774013296
>> > id ( b )
140640774013296
>> > a = 257
>> > b = 257
>> > id ( a )
140640774013392
>> > id ( b )
140640774013488
` ` `
2017-08-30 21:29:05 +03:00
* When a and b are set to ` 257 ` in the same line , the Python interpreter creates a new object , then references the second variable at the same time . If you do it on separate lines , it doesn ' t " know " that there ' s already ` 257 ` as an object .
* It ' s a compiler optimization and specifically applies to the interactive environment. When you enter two lines in a live interpreter, they ' re compiled separately , therefore optimized separately . If you were to try this example in a ` . py ` file , you would not see the same behavior , because the file is compiled all at once .
2017-08-30 20:27:10 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-09-06 19:14:59 +03:00
### `is not ...` is not `is (not ...)`
2017-08-30 21:56:48 +03:00
` ` ` py
>> > ' something ' is not None
True
>> > ' something ' is ( not None )
False
` ` `
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-30 21:56:48 +03:00
- ` is not ` is a single binary operator , and has behavior different than using ` is ` and ` not ` separated .
- ` is not ` evaluates to ` False ` if the variables on either side of the operator point to the same object and ` True ` otherwise .
- - -
2017-08-30 20:40:42 +03:00
### The function inside loop sticks to the same output
2017-08-27 21:04:18 +03:00
` ` ` py
funcs = [ ]
results = [ ]
for x in range ( 7 ) :
def some_func ( ) :
return x
funcs . append ( some_func )
results . append ( some_func ( ) )
funcs_results = [ func ( ) for func in funcs ]
` ` `
* * Output : * *
` ` ` py
>> > results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
>> > funcs_results
[ 6 , 6 , 6 , 6 , 6 , 6 , 6 ]
` ` `
2017-08-30 20:27:10 +03:00
Even when the values of ` x ` were different in every iteration prior to appending ` some_func ` to ` funcs ` , all the functions return 6.
2017-08-27 21:04:18 +03:00
/ / OR
` ` ` py
>> > powers_of_x = [ lambda x : x * * i for i in range ( 10 ) ]
>> > [ f ( 2 ) for f in powers_of_x ]
[ 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 , 512 ]
` ` `
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-27 21:04:18 +03:00
2017-08-30 20:27:10 +03:00
- When defining a function inside a loop that uses the loop variable in its body , the loop function ' s closure is bound to the variable, not its value. So all of the functions use the latest value assigned to the variable for computation.
2017-08-27 21:04:18 +03:00
2017-08-31 14:31:41 +03:00
- To get the desired behavior you can pass in the loop variable as a named variable to the function . * * Why this works ? * * Because this will define the variable again within the function ' s scope.
2017-08-27 21:04:18 +03:00
2017-08-30 20:27:10 +03:00
` ` ` py
funcs = [ ]
for x in range ( 7 ) :
def some_func ( x = x ) :
return x
funcs . append ( some_func )
` ` `
2017-08-27 21:04:18 +03:00
2017-08-30 20:27:10 +03:00
* * Output : * *
` ` ` py
>> > funcs_results = [ func ( ) for func in funcs ]
>> > funcs_results
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 ]
` ` `
2017-08-27 21:04:18 +03:00
2017-08-27 23:05:30 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Loop variables leaking out of local scope!
2017-08-27 23:05:30 +03:00
2017-08-30 20:27:10 +03:00
1 \.
2017-08-27 23:05:30 +03:00
` ` ` py
for x in range ( 7 ) :
if x == 6 :
print ( x , ' : for x inside loop ' )
print ( x , ' : x in global ' )
` ` `
* * Output : * *
` ` ` py
6 : for x inside loop
6 : x in global
` ` `
2017-08-30 21:29:05 +03:00
But ` x ` was never defined outside the scope of for loop . . .
2017-08-27 23:05:30 +03:00
2017-08-30 20:27:10 +03:00
2 \.
2017-08-27 23:05:30 +03:00
` ` ` py
# This time let's initialize x first
x = - 1
for x in range ( 7 ) :
if x == 6 :
print ( x , ' : for x inside loop ' )
print ( x , ' : x in global ' )
` ` `
* * Output : * *
` ` ` py
6 : for x inside loop
6 : x in global
` ` `
2017-08-30 20:27:10 +03:00
3 \.
2017-08-27 23:05:30 +03:00
` ` `
x = 1
print ( [ x for x in range ( 5 ) ] )
print ( x , ' : x in global ' )
` ` `
* * Output ( on Python 2. x ) : * *
` ` `
[ 0 , 1 , 2 , 3 , 4 ]
( 4 , ' : x in global ' )
` ` `
* * Output ( on Python 3. x ) : * *
` ` `
[ 0 , 1 , 2 , 3 , 4 ]
1 : x in global
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 23:05:30 +03:00
2017-08-30 21:29:05 +03:00
- In Python , for - loops use the scope they exist in and leave their defined loop - variable behind . This also applies if we explicitly defined the for - loop variable in the global namespace before . In this case , it will rebind the existing variable .
2017-08-27 23:05:30 +03:00
2017-08-30 20:27:10 +03:00
- The differences in the output of Python 2. x and Python 3. x interpreters for list comprehension example can be explained by following change documented in [ What ’ s New In Python 3.0 ] ( https : / / docs . python . org / 3 / whatsnew / 3.0 . html ) documentation :
2017-08-27 23:05:30 +03:00
2017-08-30 21:29:05 +03:00
> " List comprehensions no longer support the syntactic form `[... for var in item1, item2, ...]`. Use `[... for var in (item1, item2, ...)]` instead. Also, note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a `list()` constructor, and in particular the loop control variables are no longer leaked into the surrounding scope. "
2017-08-27 23:05:30 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 21:29:05 +03:00
### A tic-tac-toe where X wins in the first attempt!
2017-08-27 21:47:59 +03:00
` ` ` py
# Let's initialize a row
row = [ " " ] * 3 #row i['', '', '']
2017-09-01 12:28:17 +03:00
# Let's make a board
2017-08-27 21:47:59 +03:00
board = [ row ] * 3
` ` `
* * Output : * *
` ` ` py
>> > board
[ [ ' ' , ' ' , ' ' ] , [ ' ' , ' ' , ' ' ] , [ ' ' , ' ' , ' ' ] ]
>> > board [ 0 ]
[ ' ' , ' ' , ' ' ]
>> > board [ 0 ] [ 0 ]
' '
>> > board [ 0 ] [ 0 ] = " X "
>> > board
[ [ ' X ' , ' ' , ' ' ] , [ ' X ' , ' ' , ' ' ] , [ ' X ' , ' ' , ' ' ] ]
` ` `
2017-08-31 14:31:41 +03:00
We didn ' t assigned 3 " X " s or did we?
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 21:47:59 +03:00
2017-08-30 21:29:05 +03:00
When we initialize ` row ` variable , this visualization explains what happens in the memory
2017-08-27 21:47:59 +03:00
! [ image ] ( / images / tic - tac - toe / after_row_initialized . png )
2017-08-30 20:27:10 +03:00
And when the ` board ` is initialized by multiplying the ` row ` , this is what happens inside the memory ( each of the elements ` board [ 0 ] ` , ` board [ 1 ] ` and ` board [ 2 ] ` is a reference to the same list referred by ` row ` )
2017-08-27 21:47:59 +03:00
! [ image ] ( / images / tic - tac - toe / after_board_initialized . png )
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Beware of default mutable arguments!
2017-08-27 21:47:59 +03:00
` ` ` py
def some_func ( default_arg = [ ] ) :
default_arg . append ( " some_string " )
return default_arg
` ` `
* * Output : * *
` ` ` py
>> > some_func ( )
[ ' some_string ' ]
>> > some_func ( )
[ ' some_string ' , ' some_string ' ]
>> > some_func ( [ ] )
[ ' some_string ' ]
2017-08-27 22:17:07 +03:00
>> > some_func ( )
[ ' some_string ' , ' some_string ' , ' some_string ' ]
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 22:17:07 +03:00
2017-08-30 20:27:10 +03:00
- The default mutable arguments of functions in Python aren ' t really initialized every time you call the function. Instead, the recently assigned value to them is used as the default value. When we explicitly passed `[]` to `some_func` as the argument, the default value of the `default_arg` variable was not used, so the function returned as expected.
2017-08-27 22:17:07 +03:00
2017-08-30 20:27:10 +03:00
` ` ` py
def some_func ( default_arg = [ ] ) :
default_arg . append ( " some_string " )
return default_arg
` ` `
2017-08-27 21:47:59 +03:00
2017-08-30 20:27:10 +03:00
* * Output : * *
` ` ` py
>> > some_func . __defaults__ #This will show the default argument values for the function
( [ ] , )
>> > some_func ( )
>> > some_func . __defaults__
( [ ' some_string ' ] , )
2017-09-01 12:28:17 +03:00
>> > some_func ( )
2017-08-30 20:27:10 +03:00
>> > some_func . __defaults__
( [ ' some_string ' , ' some_string ' ] , )
>> > some_func ( [ ] )
>> > some_func . __defaults__
( [ ' some_string ' , ' some_string ' ] , )
` ` `
2017-08-27 22:17:07 +03:00
2017-09-02 01:05:03 +03:00
- A common practice to avoid bugs due to mutable arguments is to assign ` None ` as the default value and later check if any value is passed to the function corresponding to that argument . Example :
2017-08-27 22:28:58 +03:00
2017-08-30 20:27:10 +03:00
` ` ` py
def some_func ( default_arg = None ) :
if not default_arg :
default_arg = [ ]
default_arg . append ( " some_string " )
return default_arg
` ` `
2017-08-27 22:28:58 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-31 16:06:48 +03:00
### Same operands, different story!
1 \.
` ` ` py
a = [ 1 , 2 , 3 , 4 ]
b = a
a = a + [ 5 , 6 , 7 , 8 ]
` ` `
* * Output : * *
` ` ` py
>> > a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
>> > b
[ 1 , 2 , 3 , 4 ]
` ` `
2017-08-27 22:17:07 +03:00
2017-08-31 16:06:48 +03:00
2 \.
` ` ` py
a = [ 1 , 2 , 3 , 4 ]
b = a
a + = [ 5 , 6 , 7 , 8 ]
` ` `
* * Output : * *
` ` ` py
>> > a
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
>> > b
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
` ` `
#### 💡 Explanation:
2017-09-06 11:30:25 +03:00
* ` a + = b ` doesn ' t always behave the same way as `a = a + b`. Classes *may* implement the *`op=`* operators differently, and lists do this.
2017-08-31 16:06:48 +03:00
2017-09-06 09:12:37 +03:00
* The expression ` a = a + [ 5 , 6 , 7 , 8 ] ` generates a new list and sets ` a ` ' s reference to that new list, leaving `b` unchanged.
2017-08-31 16:06:48 +03:00
2017-09-06 09:12:37 +03:00
* The expression ` a + = [ 5 , 6 , 7 , 8 ] ` is actually mapped to an " extend " function that operates on the list such that ` a ` and ` b ` still point to the same list that has been modified in - place .
2017-08-31 16:06:48 +03:00
- - -
### Mutating the immutable!
2017-08-27 22:17:07 +03:00
` ` ` py
some_tuple = ( " A " , " tuple " , " with " , " values " )
another_tuple = ( [ 1 , 2 ] , [ 3 , 4 ] , [ 5 , 6 ] )
` ` `
* * Output : * *
` ` ` py
>> > some_tuple [ 2 ] = " change this "
TypeError : ' tuple ' object does not support item assignment
>> > another_tuple [ 2 ] . append ( 1000 ) #This throws no error
>> > another_tuple
( [ 1 , 2 ] , [ 3 , 4 ] , [ 5 , 6 , 1000 ] )
2017-08-30 10:14:41 +03:00
>> > another_tuple [ 2 ] + = [ 99 , 999 ]
TypeError : ' tuple ' object does not support item assignment
>> > another_tuple
( [ 1 , 2 ] , [ 3 , 4 ] , [ 5 , 6 , 1000 , 99 , 999 ] )
2017-08-27 22:17:07 +03:00
` ` `
2017-08-31 16:06:48 +03:00
But I thought tuples were immutable . . .
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-27 22:17:07 +03:00
2017-08-30 10:14:41 +03:00
* Quoting from https : / / docs . python . org / 2 / reference / datamodel . html
2017-08-27 21:47:59 +03:00
2017-08-30 20:27:10 +03:00
> Immutable sequences
2017-08-30 21:29:05 +03:00
An object of an immutable sequence type cannot change once it is created . ( If the object contains references to other objects , these other objects may be mutable and may be modified ; however , the collection of objects directly referenced by an immutable object cannot change . )
2017-08-27 21:47:59 +03:00
2017-08-30 10:14:41 +03:00
* ` + = ` operator changes the list in - place . The item assignment doesn ' t work, but when the exception occurs, the item has already been changed in place.
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Using a variable not defined in scope
2017-08-27 23:05:30 +03:00
` ` ` py
a = 1
def some_func ( ) :
return a
def another_func ( ) :
a + = 1
return a
` ` `
* * Output : * *
` ` ` py
>> > some_func ( )
1
>> > another_func ( )
UnboundLocalError : local variable ' a ' referenced before assignment
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
* When you make an assignment to a variable in a scope , it becomes local to that scope . So ` a ` becomes local to the scope of ` another_func ` , but it has not been initialized previously in the same scope which throws an error .
* Read [ this ] ( http : / / sebastianraschka . com / Articles / 2014 _python_scope_and_namespaces . html ) short but an awesome guide to learn more about how namespaces and scope resolution works in Python .
* To modify the outer scope variable ` a ` in ` another_func ` , use ` global ` keyword .
2017-08-30 00:42:17 +03:00
` ` ` py
def another_func ( )
global a
a + = 1
return a
` ` `
2017-08-31 14:31:41 +03:00
2017-08-30 00:42:17 +03:00
* * Output : * *
` ` ` py
>> > another_func ( )
2
` ` `
2017-08-27 23:05:30 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### The disappearing variable from outer scope
2017-08-28 19:07:06 +03:00
` ` ` py
e = 7
try :
raise Exception ( )
except Exception as e :
pass
` ` `
* * Output ( Python 2. x ) : * *
` ` ` py
>> > print ( e )
# prints nothing
` ` `
* * Output ( Python 3. x ) : * *
` ` ` py
>> > print ( e )
NameError : name ' e ' is not defined
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-28 19:07:06 +03:00
* Source : https : / / docs . python . org / 3 / reference / compound_stmts . html #except
2017-08-30 21:29:05 +03:00
When an exception has been assigned using ` as ` target , it is cleared at the end of the except clause . This is as if
2017-08-28 19:07:06 +03:00
` ` ` py
except E as N :
foo
` ` `
was translated to
` ` ` py
except E as N :
try :
foo
finally :
del N
` ` `
2017-08-30 21:29:05 +03:00
This means the exception must be assigned to a different name to be able to refer to it after the except clause . Exceptions are cleared because , with the traceback attached to them , they form a reference cycle with the stack frame , keeping all locals in that frame alive until the next garbage collection occurs .
2017-08-28 19:07:06 +03:00
2017-08-30 21:29:05 +03:00
* The clauses are not scoped in Python . Everything in the example is present in the same scope , and the variable ` e ` got removed due to the execution of the ` except ` clause . The same is not the case with functions which have their separate inner - scopes . The example below illustrates this :
2017-08-28 19:07:06 +03:00
2017-08-30 20:27:10 +03:00
` ` ` py
def f ( x ) :
del ( x )
print ( x )
2017-08-30 21:56:48 +03:00
2017-08-30 20:27:10 +03:00
x = 5
y = [ 5 , 4 , 3 ]
` ` `
* * Output : * *
` ` ` py
>> > f ( x )
UnboundLocalError : local variable ' x ' referenced before assignment
>> > f ( y )
UnboundLocalError : local variable ' x ' referenced before assignment
>> > x
5
>> > y
[ 5 , 4 , 3 ]
` ` `
2017-08-28 19:07:06 +03:00
* In Python 2. x the variable name ` e ` gets assigned to ` Exception ( ) ` instance , so when you try to print , it prints nothing .
2017-08-30 20:27:10 +03:00
* * Output ( Python 2. x ) : * *
` ` ` py
>> > e
Exception ( )
>> > print e
# Nothing is printed!
` ` `
2017-08-28 19:07:06 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Return return everywhere!
2017-08-28 19:07:06 +03:00
` ` ` py
def some_func ( ) :
try :
return ' from_try '
finally :
return ' from_finally '
` ` `
* * Output : * *
` ` ` py
>> > some_func ( )
' from_finally '
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-28 19:07:06 +03:00
2017-08-30 21:56:48 +03:00
- When a ` return ` , ` break ` or ` continue ` statement is executed in the ` try ` suite of a " try…finally " statement , the ` finally ` clause is also executed ‘ on the way out .
2017-08-30 20:27:10 +03:00
- The return value of a function is determined by the last ` return ` statement executed . Since the ` finally ` clause always executes , a ` return ` statement executed in the ` finally ` clause will always be the last one executed .
2017-08-28 19:07:06 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-31 14:31:41 +03:00
2017-08-30 20:40:42 +03:00
### When True is actually False
2017-08-28 19:07:06 +03:00
2017-08-28 20:33:10 +03:00
` ` ` py
2017-08-31 20:40:47 +03:00
True = False
2017-08-28 20:33:10 +03:00
if True == False :
print ( " I ' ve lost faith in truth! " )
` ` `
* * Output : * *
` ` `
I ' ve lost faith in truth!
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-28 20:33:10 +03:00
2017-08-30 21:29:05 +03:00
- Initially , Python used to have no ` bool ` type ( people used 0 for false and non - zero value like 1 for true ) . Then they added ` True ` , ` False ` , and a ` bool ` type , but , for backward compatibility , they couldn ' t make `True` and `False` constants- they just were built-in variables.
2017-08-31 14:31:41 +03:00
- Python 3 was backwards - incompatible , so it was now finally possible to fix that , and so this example won ' t work with Python 3.x!
2017-08-28 20:33:10 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-28 23:19:09 +03:00
2017-08-30 20:40:42 +03:00
### Be careful with chained operations
2017-08-28 19:07:06 +03:00
` ` ` py
>> > True is False == False
False
>> > False is False is False
True
2017-08-28 20:33:10 +03:00
>> > 1 > 0 < 1
True
>> > ( 1 > 0 ) < 1
False
>> > 1 > ( 0 < 1 )
False
2017-08-28 19:07:06 +03:00
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-28 20:33:10 +03:00
As per https : / / docs . python . org / 2 / reference / expressions . html #not-in
> Formally , if a , b , c , . . . , y , z are expressions and op1 , op2 , . . . , opN are comparison operators , then a op1 b op2 c . . . y opN z is equivalent to a op1 b and b op2 c and . . . y opN z , except that each expression is evaluated at most once .
2017-08-30 16:00:01 +03:00
While such behavior might seem silly to you in the above examples , it ' s fantastic with stuff like `a == b == c` and `0 <= x <= 100`.
2017-08-28 20:33:10 +03:00
* ` False is False is False ` is equivalent to ` ( False is False ) and ( False is False ) `
* ` True is False == False ` is equivalent to ` True is False and False == False ` and since the first part of the statement ( ` True is False ` ) evaluates to ` False ` , the overall expression evaluates to ` False ` .
* ` 1 > 0 < 1 ` is equivalent to ` 1 > 0 and 0 < 1 ` which evaluates to ` True ` .
* The expression ` ( 1 > 0 ) < 1 ` is equivalent to ` True < 1 ` and
` ` ` py
>> > int ( True )
1
2017-08-28 21:04:00 +03:00
>> > True + 1 #not relevant for this example, but just for fun
2
2017-08-28 20:33:10 +03:00
` ` `
So , ` 1 < 1 ` evaluates to ` False `
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Name resolution ignoring class scope
2017-08-28 23:19:09 +03:00
2017-08-30 20:27:10 +03:00
1 \.
2017-08-28 23:19:09 +03:00
` ` ` py
x = 5
class SomeClass :
x = 17
y = ( x for i in range ( 10 ) )
` ` `
* * Output : * *
` ` ` py
>> > list ( SomeClass . y ) [ 0 ]
5
` ` `
2017-08-30 20:27:10 +03:00
2 \.
2017-08-28 23:19:09 +03:00
` ` ` py
x = 5
class SomeClass :
x = 17
y = [ x for i in range ( 10 ) ]
` ` `
* * Output ( Python 2. x ) : * *
` ` ` py
>> > SomeClass . y [ 0 ]
17
` ` `
* * Output ( Python 3. x ) : * *
` ` ` py
>> > SomeClass . y [ 0 ]
5
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation
2017-08-28 23:19:09 +03:00
- Scopes nested inside class definition ignore names bound at the class level .
- A generator expression has its own scope .
2017-08-30 20:27:10 +03:00
- Starting from Python 3. X , list comprehensions also have their own scope .
2017-08-28 23:19:09 +03:00
2017-08-30 20:40:42 +03:00
- - -
### From filled to None in one instruction...
2017-08-29 22:10:21 +03:00
` ` ` py
some_list = [ 1 , 2 , 3 ]
some_dict = {
" key_1 " : 1 ,
" key_2 " : 2 ,
" key_3 " : 3
}
some_list = some_list . append ( 4 )
some_dict = some_dict . update ( { " key_4 " : 4 } )
` ` `
* * Output : * *
` ` ` py
>> > print ( some_list )
None
>> > print ( some_dict )
None
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation
2017-08-29 22:10:21 +03:00
2017-08-30 21:29:05 +03:00
Most methods that modify the items of sequence / mapping objects like ` list . append ` , ` dict . update ` , ` list . sort ` , etc . modify the objects in - place and return ` None ` . The rationale behind this is to improve performance by avoiding making a copy of the object if the operation can be done in - place ( Referred from [ here ] ( http : / / docs . python . org / 2 / faq / design . html #why-doesn-t-list-sort-return-the-sorted-list))
2017-08-29 22:10:21 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-29 22:10:21 +03:00
2017-08-30 20:40:42 +03:00
### Explicit typecast of strings
2017-08-30 00:42:17 +03:00
2017-08-31 14:31:41 +03:00
This is not a WTF at all , but it took me so long to realize such things existed in Python . So sharing it here for the beginners .
2017-08-30 00:42:17 +03:00
` ` ` py
a = float ( ' inf ' )
b = float ( ' nan ' )
c = float ( ' -iNf ' ) #These strings are case-insensitive
d = float ( ' nan ' )
` ` `
* * Output : * *
` ` ` py
>> > a
inf
>> > b
nan
>> > c
- inf
>> > float ( ' some_other_string ' )
ValueError : could not convert string to float : some_other_string
>> > a == - c #inf==inf
True
2017-09-01 11:22:18 +03:00
>> > None == None # None==None
True
2017-08-30 00:42:17 +03:00
>> > b == d #but nan!=nan
False
>> > 50 / a
2017-09-01 02:02:03 +03:00
0.0
2017-08-30 00:42:17 +03:00
>> > a / a
nan
>> > 23 + b
nan
` ` `
2017-08-30 21:29:05 +03:00
#### 💡 Explanation:
2017-08-30 00:42:17 +03:00
2017-08-30 21:29:05 +03:00
` ' inf ' ` and ` ' nan ' ` are special strings ( case - insensitive ) , which when explicitly type casted to ` float ` type , are used to represent mathematical " infinity " and " not a number " respectively .
2017-08-30 00:42:17 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-29 22:10:21 +03:00
2017-08-30 20:40:42 +03:00
### Class attributes and instance attributes
2017-08-30 12:22:11 +03:00
2017-08-30 20:27:10 +03:00
1 \.
2017-08-30 12:22:11 +03:00
` ` ` py
class A :
x = 1
class B ( A ) :
pass
class C ( A ) :
pass
` ` `
* * Ouptut : * *
` ` ` py
>> > A . x , B . x , C . x
( 1 , 1 , 1 )
2017-09-04 02:26:36 +03:00
>> > B . x = 2
2017-08-30 12:22:11 +03:00
>> > A . x , B . x , C . x
( 1 , 2 , 1 )
>> > A . x = 3
>> > A . x , B . x , C . x
( 3 , 2 , 3 )
>> > a = A ( )
>> > a . x , A . x
( 3 , 3 )
>> > a . x + = 1
>> > a . x , A . x
( 4 , 3 )
` ` `
2017-08-30 20:27:10 +03:00
2 \.
2017-08-30 12:22:11 +03:00
` ` ` py
class SomeClass :
some_var = 15
some_list = [ 5 ]
another_list = [ 5 ]
def __init__ ( self , x ) :
self . some_var = x + 1
self . some_list = self . some_list + [ x ]
self . another_list + = [ x ]
` ` `
* * Output : * *
` ` ` py
>> > some_obj = SomeClass ( 420 )
>> > some_obj . some_list
[ 5 , 420 ]
>> > some_obj . another_list
[ 5 , 420 ]
>> > another_obj = SomeClass ( 111 )
>> > another_obj . some_list
[ 5 , 111 ]
>> > another_obj . another_list
[ 5 , 420 , 111 ]
>> > another_obj . another_list is SomeClass . another_list
True
>> > another_obj . another_list is some_obj . another_list
True
` ` `
2017-09-02 22:57:23 +03:00
#### 💡 Explanation:
2017-08-30 12:22:11 +03:00
2017-08-30 21:29:05 +03:00
* Class variables and variables in class instances are internally handled as dictionaries of a class object . If a variable name is not found in the dictionary of the current class , the parent classes are searched for it .
* The ` + = ` operator modifies the mutable object in - place without creating a new object . So changing the attribute of one instance affects the other instances and the class attribute as well .
2017-08-30 12:22:11 +03:00
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-08-30 20:40:42 +03:00
### Catching the Exceptions!
2017-08-30 10:02:13 +03:00
` ` ` py
some_list = [ 1 , 2 , 3 ]
try :
# This should raise an ``IndexError``
print ( some_list [ 4 ] )
except IndexError , ValueError :
print ( " Caught! " )
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except IndexError , ValueError :
print ( " Caught again! " )
` ` `
* * Output ( Python 2. x ) : * *
` ` ` py
Caught !
ValueError : list . remove ( x ) : x not in list
` ` `
* * Output ( Python 3. x ) : * *
` ` ` py
2017-08-30 10:14:41 +03:00
File " <input> " , line 3
2017-08-30 10:02:13 +03:00
except IndexError , ValueError :
^
SyntaxError : invalid syntax
` ` `
2017-09-02 22:57:23 +03:00
#### 💡 Explanation
2017-08-30 10:02:13 +03:00
* To add multiple Exceptions to the except clause , you need to pass them as parenthesized tuple as the first argument . The second argument is an optional name , which when supplied will bind the Exception instance that has been raised . Example ,
` ` ` py
some_list = [ 1 , 2 , 3 ]
try :
# This should raise a ``ValueError``
some_list . remove ( 4 )
except ( IndexError , ValueError ) , e :
print ( " Caught again! " )
print ( e )
` ` `
* * Output ( Python 2. x ) : * *
` ` `
Caught again !
list . remove ( x ) : x not in list
` ` `
* * Output ( Python 3. x ) : * *
` ` ` py
2017-08-30 10:14:41 +03:00
File " <input> " , line 4
2017-08-30 10:02:13 +03:00
except ( IndexError , ValueError ) , e :
^
IndentationError : unindent does not match any outer indentation level
` ` `
* Separating the exception from the variable with a comma is deprecated and does not work in Python 3 ; the correct way is to use ` as ` . Example ,
` ` ` py
some_list = [ 1 , 2 , 3 ]
try :
some_list . remove ( 4 )
except ( IndexError , ValueError ) as e :
print ( " Caught again! " )
print ( e )
` ` `
* * Output : * *
` ` `
Caught again !
list . remove ( x ) : x not in list
` ` `
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 12:22:11 +03:00
2017-08-30 21:56:48 +03:00
### Midnight time doesn't exist?
2017-08-30 12:22:11 +03:00
` ` ` py
2017-08-30 21:56:48 +03:00
from datetime import datetime
2017-08-30 12:22:11 +03:00
2017-08-30 21:56:48 +03:00
midnight = datetime ( 2018 , 1 , 1 , 0 , 0 )
midnight_time = midnight . time ( )
2017-08-30 12:22:11 +03:00
2017-08-30 21:56:48 +03:00
noon = datetime ( 2018 , 1 , 1 , 12 , 0 )
noon_time = noon . time ( )
2017-08-30 12:22:11 +03:00
2017-08-30 21:56:48 +03:00
if midnight_time :
print ( " Time at midnight is " , midnight_time )
if noon_time :
print ( " Time at noon is " , noon_time )
2017-08-30 12:22:11 +03:00
` ` `
* * Output : * *
2017-08-30 21:56:48 +03:00
` ` ` sh
( ' Time at noon is ' , datetime . time ( 12 , 0 ) )
` ` `
The midnight time is not printed .
#### 💡 Explanation:
2017-09-02 15:09:50 +03:00
Before Python 3.5 , the boolean value for ` datetime . time ` object was considered to be ` False ` if it represented midnight in UTC . It is error - prone when using the ` if obj : ` syntax to check if the ` obj ` is null or some equivalent of " empty. "
2017-08-30 21:56:48 +03:00
- - -
2017-09-04 20:46:31 +03:00
### What's wrong with booleans?
2017-09-01 20:17:05 +03:00
2017-09-04 20:46:31 +03:00
1 \.
2017-09-01 20:17:05 +03:00
` ` ` py
# A simple example to count the number of boolean and
# integers in an iterable of mixed data types.
mixed_list = [ False , 1.0 , " some_string " , 3 , True , [ ] , False ]
integers_found_so_far = 0
booleans_found_so_far = 0
for item in mixed_list :
if isinstance ( item , int ) :
integers_found_so_far + = 1
elif isinstance ( item , bool ) :
booleans_found_so_far + = 1
` ` `
2017-10-17 11:24:59 +03:00
* * Output : * *
2017-09-01 20:17:05 +03:00
` ` ` py
>> > booleans_found_so_far
0
>> > integers_found_so_far
4
` ` `
2017-09-04 20:46:31 +03:00
2 \.
` ` ` py
another_dict = { }
another_dict [ True ] = " JavaScript "
another_dict [ 1 ] = " Ruby "
another_dict [ 1.0 ] = " Python "
` ` `
* * Output : * *
` ` ` py
>> > another_dict [ True ]
" Python "
` ` `
2017-09-01 20:17:05 +03:00
#### 💡 Explanation:
* Booleans are a subclass of ` int `
` ` ` py
>> > isinstance ( True , int )
True
>> > isinstance ( False , int )
True
` ` `
2017-09-04 20:46:31 +03:00
* The integer value of ` True ` is ` 1 ` and that of ` False ` is ` 0 ` .
` ` ` py
>> > True == 1 == 1.0 and False == 0 == 0.0
True
` ` `
2017-09-01 20:17:05 +03:00
* See this StackOverflow [ answer ] ( https : / / stackoverflow . com / a / 8169049 / 4354153 ) for rationale behind it .
- - -
2017-08-30 21:56:48 +03:00
### Needle in a Haystack
2017-08-31 16:06:48 +03:00
Almost every Python programmer would have faced this situation .
2017-08-30 12:22:11 +03:00
` ` ` py
2017-08-30 21:56:48 +03:00
t = ( ' one ' , ' two ' )
for i in t :
print ( i )
t = ( ' one ' )
for i in t :
print ( i )
t = ( )
print ( t )
2017-08-30 12:22:11 +03:00
` ` `
2017-08-30 21:56:48 +03:00
* * Output : * *
` ` ` py
one
two
o
n
e
tuple ( )
` ` `
2017-08-30 12:22:11 +03:00
2017-08-30 00:42:17 +03:00
2017-08-30 21:56:48 +03:00
#### 💡 Explanation:
* The correct statement for expected behavior is ` t = ( ' one ' , ) ` or ` t = ' one ' , ` ( missing comma ) otherwise the interpreter considers ` t ` to be a ` str ` and iterates over it character by character .
* ` ( ) ` is a special token and denotes empty ` tuple ` .
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 21:56:48 +03:00
2017-10-11 15:12:45 +03:00
### yielding None
Suggested by @chris - rands in [ this ] ( https : / / github . com / satwikkansal / wtfpython / issues / 32 ) issue .
` ` ` py
some_iterable = ( ' a ' , ' b ' )
def some_func ( val ) :
return " something "
` ` `
* * Output : * *
` ` ` py
>> > [ x for x in some_iterable ]
[ ' a ' , ' b ' ]
>> > [ ( yield x ) for x in some_iterable ]
< generator object < listcomp > at 0x7f70b0a4ad58 >
>> > list ( [ ( yield x ) for x in some_iterable ] )
[ ' a ' , ' b ' ]
>> > list ( ( yield x ) for x in some_iterable )
[ ' a ' , None , ' b ' , None ]
>> > list ( some_func ( ( yield x ) ) for x in some_iterable )
[ ' a ' , ' something ' , ' b ' , ' something ' ]
` ` `
#### 💡 Explanation:
- Source and explanation can be found here : https : / / stackoverflow . com / questions / 32139885 / yield - in - list - comprehensions - and - generator - expressions
- Related bug report : http : / / bugs . python . org / issue10544
- - -
2017-09-06 14:10:38 +03:00
### The surprising comma
Suggested by @MostAwesomeDude in [ this ] ( https : / / github . com / satwikkansal / wtfPython / issues / 1 ) issue .
* * Output : * *
` ` ` py
>> > def f ( x , y , ) :
. . . print ( x , y )
. . .
>> > def g ( x = 4 , y = 5 , ) :
. . . print ( x , y )
. . .
>> > def h ( x , * * kwargs , ) :
File " <stdin> " , line 1
def h ( x , * * kwargs , ) :
^
SyntaxError : invalid syntax
>> > def h ( * args , ) :
File " <stdin> " , line 1
def h ( * args , ) :
^
SyntaxError : invalid syntax
` ` `
#### 💡 Explanation:
- Trailing comma is not always legal in formal parameters list of a Python function .
- In Python , the argument list is defined partially with leading commas and partially with trailing commas . This conflict causes situations where a comma is trapped in the middle , and no rule accepts it .
- * * Note : * * The trailing comma problem is [ fixed in Python 3.6 ] ( https : / / bugs . python . org / issue9232 ) . The remarks in [ this ] ( https : / / bugs . python . org / issue9232 #msg248399) post discuss in brief different usages of trailing commas in Python.
- - -
2017-09-04 22:22:19 +03:00
### For what?
Suggested by @MittalAshok in [ this ] ( https : / / github . com / satwikkansal / wtfpython / issues / 23 ) issue .
` ` ` py
some_string = " wtf "
some_dict = { }
for i , some_dict [ i ] in enumerate ( some_string ) :
pass
` ` `
2017-10-17 11:24:59 +03:00
* * Output : * *
2017-09-04 22:22:19 +03:00
` ` ` py
>> > some_dict # An indexed dict is created.
2017-09-05 13:27:29 +03:00
{ 0 : ' w ' , 1 : ' t ' , 2 : ' f ' }
2017-09-04 22:22:19 +03:00
` ` `
#### 💡 Explanation:
* A ` for ` statement is defined in the [ Python grammar ] ( https : / / docs . python . org / 3 / reference / grammar . html ) as :
` ` `
for_stmt : ' for ' exprlist ' in ' testlist ' : ' suite [ ' else ' ' : ' suite ]
` ` `
Where ` exprlist ` is the assignment target . This means that the equivalent of ` { exprlist } = { next_value } ` is * * executed for each item * * in the iterable .
An interesting example suggested by @tukkek in [ this ] ( https : / / github . com / satwikkansal / wtfPython / issues / 11 ) issue illustrates this :
` ` ` py
for i in range ( 4 ) :
print ( i )
i = 10
` ` `
* * Output : * *
` ` `
0
1
2
3
` ` `
Did you expect the loop to run just once ?
* * 💡 Explanation : * *
- The assignment statement ` i = 10 ` never affects the iterations of the loop because of the way for loops work in Python . Before the beginning of every iteration , the next item provided by the iterator ( ` range ( 4 ) ` this case ) is unpacked and assigned the target list variables ( ` i ` in this case ) .
* The ` enumerate ( some_string ) ` function yields a new value ` i ` ( A counter going up ) and a character from the ` some_string ` in each iteration . It then sets the ( just assigned ) ` i ` key of the dictionary ` some_dict ` to that character . The unrolling of the loop can be simplified as :
` ` ` py
>> > i , some_dict [ i ] = ( 0 , ' w ' )
>> > i , some_dict [ i ] = ( 1 , ' t ' )
>> > i , some_dict [ i ] = ( 2 , ' f ' )
>> > some_dict
` ` `
- - -
2017-09-04 20:01:18 +03:00
### not knot!
Suggested by @MostAwesomeDude in [ this ] ( https : / / github . com / satwikkansal / wtfPython / issues / 1 ) issue .
` ` ` py
x = True
y = False
` ` `
* * Output : * *
` ` ` py
>> > not x == y
True
>> > x == not y
File " <input> " , line 1
x == not y
^
SyntaxError : invalid syntax
` ` `
#### 💡 Explanation:
* Operator precedence affects how an expression is evaluated , and ` == ` operator has higher precedence than ` not ` operator in Python .
* So ` not x == y ` is equivalent to ` not ( x == y ) ` which is equivalent to ` not ( True == False ) ` finally evaluating to ` True ` .
* But ` x == not y ` raises a ` SyntaxError ` because it can be thought of being equivalent to ` ( x == not ) y ` and not ` x == ( not y ) ` which you might have expected at first sight .
* The parser expected the ` not ` token to be a part of the ` not in ` operator ( because both ` == ` and ` not in ` operators have same precedence ) , but after not being able to find a ` in ` token following the ` not ` token , it raises a ` SyntaxError ` .
- - -
2018-01-07 08:36:47 +03:00
### Subclass relationships
Suggested by @Lucas - C in [ this ] ( https : / / github . com / satwikkansal / wtfpython / issues / 36 ) issue .
* * Output : * *
` ` ` py
>> > from collections import Hashable
>> > issubclass ( list , object )
True
>> > issubclass ( object , Hashable )
True
>> > issubclass ( list , Hashable )
False
` ` `
The Subclass relationships were expected to be transitive , right ? ( i . e . if ` A ` is a subclass of ` B ` , and ` B ` is a subclass of ` C ` , the ` A ` _should_ a subclass of ` C ` )
#### 💡 Explanation:
* Subclass relationships are not necessarily transitive in Python . Anyone is allowed to define their own , arbitrary ` __subclasscheck__ ` in a metaclass .
* When ` issubclass ( cls , Hashable ) ` is called , it simply looks for non - Falsey " `__hash__` " method in ` cls ` or anything it inherits from .
* Since ` object ` is hashable , but ` list ` is non - hashable , it breaks the transitivity relation .
* More detailed explanation can be found [ here ] ( https : / / www . naftaliharris . com / blog / python - subclass - intransitivity / ) .
- - -
2018-01-07 08:55:34 +03:00
### Mangling time!
Suggested by @Lucas - C in [ this ] ( https : / / github . com / satwikkansal / wtfpython / issues / 36 ) issue .
` ` ` py
class Yo ( object ) :
def __init__ ( self ) :
self . __honey = True
self . bitch = True
` ` `
* * Output : * *
` ` ` py
2018-01-07 08:57:23 +03:00
>> > Yo ( ) . bitch
2018-01-07 08:55:34 +03:00
True
2018-01-07 08:57:23 +03:00
>> > Yo ( ) . __honey
2018-01-07 08:55:34 +03:00
AttributeError : ' Yo ' object has no attribute ' __honey '
>> > Yo ( ) . _Yo__honey
True
` ` `
Why did ` Yo ( ) . _Yo__honey ` worked ? Only Indian readers would understand .
#### 💡 Explanation:
* [ Name Mangling ] ( https : / / en . wikipedia . org / wiki / Name_mangling ) is used to avoid naming collisions between different namespaces .
* In Python , the interpreter modifies ( mangles ) the class member names starting with ` __ ` ( double underscore ) and not ending with more than one trailing underscore by addding ` _NameOfTheClass ` in frot .
* So , to access ` __honey ` attribute , we are required to append ` _Yo ` to the front which would prevent conflicts with a same name attribute defined in any other class .
- - -
2017-09-01 01:19:20 +03:00
### Let's see if you can guess this?
2017-09-01 01:55:24 +03:00
Suggested by @PiaFraus in [ this ] ( https : / / github . com / satwikkansal / wtfPython / issues / 9 ) issue .
2017-09-01 01:19:20 +03:00
` ` ` py
a , b = a [ b ] = { } , 5
` ` `
2017-09-01 01:19:20 +03:00
* * Output : * *
` ` ` py
>> > a
{ 5 : ( { . . . } , 5 ) }
` ` `
#### 💡 Explanation:
* According to [ Python language reference ] ( https : / / docs . python . org / 2 / reference / simple_stmts . html #assignment-statements), assignment statements have the form
` ` `
( target_list " = " ) + ( expression_list | yield_expression )
` ` `
and
> An assignment statement evaluates the expression list ( remember that this can be a single expression or a comma - separated list , the latter yielding a tuple ) and assigns the single resulting object to each of the target lists , from left to right .
* The ` + ` in ` ( target_list " = " ) + ` means there can be * * one or more * * target lists . In this case , target lists are ` a , b ` and ` a [ b ] ` ( note the expression list is exactly one , which in our case is ` { } , 5 ` ) .
* After the expression list is evaluated , it ' s value is unpacked to the target lists from **left to right**. So, in our case, first the ` {} , 5` tuple is unpacked to `a, b` and we now have `a = {} ` and `b = 5`.
* ` a ` is now assigned to ` { } ` which is a mutable object .
* The second target list is ` a [ b ] ` ( you may expect this to throw an error because both ` a ` and ` b ` have not been defined in the statements before . But remember , we just assigned ` a ` to ` { } ` and ` b ` to ` 5 ` ) .
* Now , we are setting the key ` 5 ` in the dictionary to the tuple ` ( { } , 5 ) ` creating a circular reference ( the ` { . . . } ` in the output refers to the same object that ` a ` is already referencing ) . Another simpler example of circular reference could be
` ` ` py
>> > some_list = some_list [ 0 ] = [ 0 ]
>> > some_list
[ [ . . . ] ]
>> > some_list [ 0 ]
[ [ . . . ] ]
>> > some_list is some_list [ 0 ]
2017-10-03 17:50:39 +03:00
True
2017-09-01 01:19:20 +03:00
` ` `
Similar is the case in our example ( ` a [ b ] [ 0 ] ` is the same object as ` a ` )
* So to sum it up , you can break the example down to
` ` ` py
a , b = { } , 5
a [ b ] = a , b
` ` `
And the circular reference can be justified by the fact that ` a [ b ] [ 0 ] ` is the same object as ` a `
` ` ` py
>> > a [ b ] [ 0 ] is a
True
` ` `
- - -
2017-08-30 20:40:42 +03:00
### Minor Ones
2017-08-28 19:07:06 +03:00
2017-08-30 17:17:09 +03:00
* ` join ( ) ` is a string operation instead of list operation . ( sort of counter - intuitive at first usage )
2017-08-31 16:06:48 +03:00
2017-08-30 21:29:05 +03:00
* * 💡 Explanation : * *
2017-08-31 20:21:34 +03:00
If ` join ( ) ` is a method on a string then it can operate on any iterable ( list , tuple , iterators ) . If it were a method on a list , it ' d have to be implemented separately by every type. Also, it doesn ' t make much sense to put a string - specific method on a generic ` list ` object API .
2017-08-28 21:04:00 +03:00
2017-08-30 17:17:09 +03:00
* Few weird looking but semantically correct statements :
+ ` [ ] = ( ) ` is a semantically correct statement ( unpacking an empty ` tuple ` into an empty ` list ` )
2017-09-06 13:38:42 +03:00
+ ` ' a ' [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] ` is also a semantically correct statement as strings are [ sequences ] ( https : / / docs . python . org / 3 / glossary . html #term-sequence)(iterables supporting element access using integer indices) in Python.
2017-09-02 15:09:50 +03:00
+ ` 3 - - 0 - - 5 == 8 ` and ` - - 5 == 5 ` are both semantically correct statements and evaluate to ` True ` .
2017-09-01 20:17:05 +03:00
2017-09-06 13:34:28 +03:00
* Given that ` a ` is a number , ` + + a ` and ` - - a ` are both valid Python statements , but don ' t behave the same way as compared with similar statements in languages like C, C++ or Java.
` ` ` py
>> > a = 5
>> > a
5
>> > + + a
5
>> > - - a
5
` ` `
* * 💡 Explanation : * *
+ There is no ` + + ` operator in Python grammar . It is actually two ` + ` operators .
+ ` + + a ` parses as ` + ( + a ) ` which translates to ` a ` . Similarly , the output of the statement ` - - a ` can be justified .
+ This StackOverflow [ thread ] ( https : / / stackoverflow . com / questions / 3654830 / why - are - there - no - and - operators - in - python ) discusses the rationale behind the absence of increment and decrement operators in Python .
2017-08-30 21:29:05 +03:00
* Python uses 2 bytes for local variable storage in functions . In theory , this means that only 65536 variables can be defined in a function . However , python has a handy solution built in that can be used to store more than 2 ^ 16 variable names . The following code demonstrates what happens in the stack when more than 65536 local variables are defined ( Warning : This code prints around 2 ^ 18 lines of text , so be prepared ! ) :
2017-08-30 20:27:10 +03:00
` ` ` py
import dis
exec ( """
def f ( ) : * """ + """
""" .join([ " X " +str(x)+ " = " + str(x) for x in range(65539)]))
2017-08-30 21:29:05 +03:00
2017-08-30 20:27:10 +03:00
f ( )
2017-08-30 21:29:05 +03:00
2017-08-30 20:27:10 +03:00
print ( dis . dis ( f ) )
` ` `
2017-08-30 16:00:01 +03:00
2017-10-11 20:14:47 +03:00
* Half triple - quoted strings ( suggested by @asottile in [ this ] ( https : / / github . com / satwikkansal / wtfpython / issues / 40 ) issue ) .
` ` ` py
>> > print ( ' wtfpython ' ' ' )
wtfpython
>> > print ( " wtfpython " " " )
wtfpython
>> > # The following statements raise `SyntaxError`
>> > # print('''wtfpython')
>> > # print("""wtfpython")
` ` `
* * 💡 Explanation : * *
+ Python support implicit [ string literal concatenation ] ( https : / / docs . python . org / 2 / reference / lexical_analysis . html #string-literal-concatenation), Example,
` ` `
>> > print ( " wtf " " python " )
wtfpython
>> > print ( " wtf " " " ) # or "wtf"""
wtf
` ` `
+ ` ''' ` and ` " " " ` are also string delimiters in Python which causes a SyntaxError because the Python interpreter was expecting a terminating triple quote as delimiter while scanning the currently encountered triple quoted string literal.
2017-09-06 13:50:54 +03:00
* Multiple Python threads won ' t run your *Python code* concurrently (yes you heard it right!). It may seem intuitive to spawn several threads and let them execute your Python code concurrently, but, because of the [Global Interpreter Lock](https://wiki.python.org/moin/GlobalInterpreterLock) in Python, all you ' re doing is making your threads execute on the same core turn by turn . Python threads are good for IO - bound tasks , but to achieve actual parallelization in Python for CPU - bound tasks , you might want to use the Python [ multiprocessing ] ( https : / / docs . python . org / 2 / library / multiprocessing . html ) module .
2017-08-30 19:12:15 +03:00
2017-08-31 16:06:48 +03:00
* List slicing with out of the bounds indices throws no errors
2017-08-30 16:00:01 +03:00
` ` ` py
>> > some_list = [ 1 , 2 , 3 , 4 , 5 ]
>> > some_list [ 111 : ]
[ ]
` ` `
2017-08-30 20:40:42 +03:00
- - -
2017-08-30 20:27:10 +03:00
2017-08-30 17:17:09 +03:00
2017-08-30 12:22:11 +03:00
# TODO: Hell of an example!
2017-08-30 20:27:10 +03:00
Trying to come up with an example that combines multiple examples discussed above , making it difficult for the reader to guess the output correctly : sweat_smile : .
2017-08-28 20:33:10 +03:00
2017-08-26 21:31:11 +03:00
# Contributing
2017-10-11 13:25:32 +03:00
All patches are Welcome ! Filing an [ issue ] ( https : / / github . com / satwikkansal / wtfpython / issues / new ) first before submitting a patch will be appreciated : ) Please see [ CONTRIBUTING . md ] ( / CONTRIBUTING . md ) for further details .
For discussions , either create a new [ issue ] ( https : / / github . com / satwikkansal / wtfpython / issues / new ) or ping on the Gitter [ channel ] ( https : / / gitter . im / wtfpython / Lobby )
2017-08-26 21:31:11 +03:00
# Acknowledgements
2017-08-31 16:06:48 +03:00
The idea and design for this collection are inspired by Denys Dovhan ' s awesome project [wtfjs](https://github.com/denysdovhan/wtfjs).
2017-08-26 21:31:11 +03:00
2017-08-30 20:27:10 +03:00
#### Some nice Links!
2017-08-27 23:11:15 +03:00
* https : / / www . youtube . com / watch ? v = sH4XF6pKKmk
2017-08-28 23:19:09 +03:00
* https : / / www . reddit . com / r / Python / comments / 3 cu6ej / what_are_some_wtf_things_about_python
* https : / / sopython . com / wiki / Common_Gotchas_In_Python
2017-08-29 22:10:21 +03:00
* https : / / stackoverflow . com / questions / 530530 / python - 2 - x - gotchas - and - landmines
2017-08-30 21:29:05 +03:00
* https : / / stackoverflow . com / questions / 1011431 / common - pitfalls - in - python ( This StackOverflow thread has some DOs and DONTs which are worth reading . )
2017-08-26 21:31:11 +03:00
2017-12-23 22:33:36 +03:00
2017-08-26 21:31:11 +03:00
# 🎓 License
[ ! [ CC 4.0 ] [ license - image ] ] [ license - url ]
& copy ; [ Satwik Kansal ] ( https : / / satwikkansal . xyz )
[ license - url ] : http : / / www . wtfpl . net
[ license - image ] : https : / / img . shields . io / badge / License - WTFPL % 202.0 - lightgrey . svg ? style = flat - square
2017-12-23 22:33:36 +03:00
# Donation
This project costed me nothing and doesn ' t require anything to operate. However, I ' m looking for full - time opportunities , I ' d **highly appreciate** if you can let me know about open positions around you.
And finally , Thanks for reading this project , I hope you enjoyed it and found it informative !