📜 ⬆️ ⬇️

What the hell is python


We recently wrote about funny, cunning, and weird JavaScript examples. Now it's Python's turn. Python, a high-level and interpreted language, has many convenient features. But sometimes the result of the work of some pieces of code at first glance looks unobvious.


Below is a fun project that collects examples of unexpected behavior in Python with a discussion of what is happening under the hood. Some examples do not fall into the category of real WTF?!, But instead they demonstrate interesting features of the language that you may want to avoid. I think this is a good way to learn the internal workings of Python, and I hope you find it interesting.


If you are already an experienced programmer in Python, then many examples may be familiar to you, and even cause nostalgia for those times when you racked your brains over them :)


Content



Example structure


Note: All examples given are tested on the Python 3.5.2 interactive interpreter and should work in all versions of the language, unless otherwise explicitly stated in the description.


Structure of examples:


Any stupid headline


 # . #   ... 

Result (Python version):


 >>> _ ,   

(Optional): A one-line description of the unexpected result.


Explanation:



PS You can also read these examples on the command line. Only first install the wtfpython npm package,


 $ npm install -g wtfpython 

Now run wtfpython on the command line, and as a result, this collection will open in your $PAGER .


#TODO: Add the pypi package for reading at the command line.


Examples


Missing lines?


Result:


 >>> value = 11 >>> valu = 32 >>> value 11 

Wat?


Note: The easiest way to reproduce this example is by copying and pasting into your file / shell.


Explanation


Some Unicode characters look the same as ASCII, but differ in interpreter.


 >>> value = 42 #ascii e >>> valu = 23 #cyrillic e, Python 2.x interpreter would raise a `SyntaxError` here >>> value 42 

Well, somehow doubtful ...


 def square(x): """        . """ sum_so_far = 0 for counter in range(x): sum_so_far = sum_so_far + x return sum_so_far 

Result (Python 2.x):


 >>> square(10) 10 

Shouldn't it have turned 100?
Note: if you cannot reproduce the result, try running the file mixed_tabs_and_spaces.py in the shell.


Explanation



Result (Python 3.x):


 TabError: inconsistent use of tabs and spaces in indentation 

Time for hash cakes!


one.


 some_dict = {} some_dict[5.5] = "Ruby" some_dict[5.0] = "JavaScript" some_dict[5] = "Python" 

Result:


 >>> some_dict[5.5] "Ruby" >>> some_dict[5.0] "Python" >>> some_dict[5] "Python" 

Python destroyed the existence of javascript?


Explanation



Processing time mismatch


 array = [1, 8, 15] g = (x for x in array if array.count(x) > 0) array = [2, 8, 22] 

Result:


 >>> print(list(g)) [8] 

Explanation



Transforming the dictionary during its iteration


 x = {0: None} for i in x: del x[i] x[i+1] = None print(i) 

Result:


 0 1 2 3 4 5 6 7 

Yes, it runs exactly eight times and stops.


Explanation:



Deleting a list item during its iteration


 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) 

Result:


 >>> list_1 [1, 2, 3, 4] >>> list_2 [2, 4] >>> list_3 [] >>> list_4 [2, 4] 

Do you know why the result was [2, 4] ?


Explanation:



Why did it happen [2, 4] ?



Backslashes at the end of the line


Result:


 >>> print("\\ some string \\") >>> print(r"\ some string") >>> print(r"\ some string \") File "<stdin>", line 1 print(r"\ some string \") ^ SyntaxError: EOL while scanning string literal 

Explanation



Let's make a giant string!


This is not WTF, but only some cool things, and they need to be wary :)


 def add_string_with_plus(iters): s = "" for i in range(iters): s += "xyz" assert len(s) == 3*iters 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 

Result:


 >>> timeit(add_string_with_plus(10000)) 100 loops, best of 3: 9.73 ms per loop >>> timeit(add_string_with_format(10000)) 100 loops, best of 3: 5.47 ms per loop >>> timeit(add_string_with_join(10000)) 100 loops, best of 3: 10.1 ms per loop >>> l = ["xyz"]*10000 >>> timeit(convert_list_to_string(l, 10000)) 10000 loops, best of 3: 75.3 µs per loop 

Explanation



String Concatenation Interpreter Optimization


 >>> 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:



Yes, it exists!


Clause else for loops . Typical example:


  def does_exists_num(l, to_find): for num in l: if num == to_find: print("Exists!") break else: print("Does not exist") 

Result:


 >>> some_list = [1, 2, 3, 4, 5] >>> does_exists_num(some_list, 4) 

Exists!


 >>> does_exists_num(some_list, -1) 

Does not exist.


Clause else in exception handling . Example:


 try: pass except: print("Exception occurred!!!") else: print("Try block executed successfully...") 

Result:


 Try block executed successfully... 

Explanation:



is not what it is


This example is very widely known.


 >>> a = 256 >>> b = 256 >>> a is b True >>> a = 257 >>> b = 257 >>> a is b False >>> a = 257; b = 257 >>> a is b True 

Explanation:


Difference between is and ==



 >>> [] == [] True >>> [] is [] # These are two empty lists at two different memory locations. False 

256 is an existing object, and 257 is not
When you start Python, numbers from -5 to 256 are placed in memory. They are used often, so it is advisable to keep them ready.


Quote from https://docs.python.org/3/c-api/long.html


The current implementation supports an array of integer objects for all numbers from –5 to 256, so that when you create an int from this range, you get a reference to an existing object. Therefore, it should be possible to change the value to 1. But I suspect that in this case the behavior of Python will be unpredictable. :-)

 >>> 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 

The interpreter was not so smart, and during execution y = 257 did not understand that we had already created an integer with the value 257 , therefore it creates another object in the memory.


a and b refer to the same object when initialized with the same value on the same line.


 >>> a, b = 257, 257 >>> id(a) 140640774013296 >>> id(b) 140640774013296 >>> a = 257 >>> b = 257 >>> id(a) 140640774013392 >>> id(b) 140640774013488 


is not ... different from is (not ...)


 >>> 'something' is not None True >>> 'something' is (not None) False 

Explanation



The function inside the loop produces the same result.


 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] 

Result:


 >>> results [0, 1, 2, 3, 4, 5, 6] >>> funcs_results [6, 6, 6, 6, 6, 6, 6] 

If before adding some_func in funcs , the x values ​​in each iteration were different, all functions returned 6.


 //OR >>> 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] 

Explanation



 funcs = [] for x in range(7): def some_func(x=x): return x funcs.append(some_func) 

Result:


 >>> funcs_results = [func() for func in funcs] >>> funcs_results [0, 1, 2, 3, 4, 5, 6] 

Loop loop variables from the local scope


one.


 for x in range(7): if x == 6: print(x, ': for x inside loop') print(x, ': x in global') 

Result:


 6 : for x inside loop 6 : x in global 

But x not defined for a loop outside the scope.


2


 # 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') 

Result:


 6 : for x inside loop 6 : x in global 

3


 x = 1 print([x for x in range(5)]) print(x, ': x in global') 

Result (on Python 2.x):


 [0, 1, 2, 3, 4] (4, ': x in global') 

Result (on Python 3.x):


 [0, 1, 2, 3, 4] 1 : x in global 

Explanation



Tic-tac-toe, where X wins on the first try


 # Let's initialize a row row = [""]*3 #row i['', '', ''] # Let's make a board board = [row]*3 

Result:


 >>> board [['', '', ''], ['', '', ''], ['', '', '']] >>> board[0] ['', '', ''] >>> board[0][0] '' >>> board[0][0] = "X" >>> board [['X', '', ''], ['X', '', ''], ['X', '', '']] 

But we did not assign three X, right?


Explanation


This visualization explains what happens in memory when the row variable is initialized:


image


And when the board initialized by multiplying the row , this is what happens in the memory (each of the board[0] , board[1] and board[2] elements is a reference to the same list specified in row ):


image


Beware of default variable arguments.


 def some_func(default_arg=[]): default_arg.append("some_string") return default_arg 

Result:


 >>> some_func() ['some_string'] >>> some_func() ['some_string', 'some_string'] >>> some_func([]) ['some_string'] >>> some_func() ['some_string', 'some_string', 'some_string'] 

Explanation



 def some_func(default_arg=[]): default_arg.append("some_string") return default_arg 

Result:


 >>> some_func.__defaults__ #This will show the default argument values for the function ([],) >>> some_func() >>> some_func.__defaults__ (['some_string'],) >>> some_func() >>> some_func.__defaults__ (['some_string', 'some_string'],) >>> some_func([]) >>> some_func.__defaults__ (['some_string', 'some_string'],) 


')

Source: https://habr.com/ru/post/337364/


All Articles