Inspired by Josh Bloch's Java Puzzlers, we put together our own Python Puzzlers. This slide deck brings you a set of 10 python puzzlers, that are fun and educational. Each puzzler will show you a piece of python code. Your task if to figure out what happens when the code is run. Whether you're a python beginner or a passionate python veteran, we hope that there's something to learn for everybody.
This slide deck was first presented at shopkick. Nandan Sawant and Ryan Rueth are engineers at shopkick. Keeping the audience in mind, most of the puzzlers are based on python 2.x.
5. Defaulter
From the python documentation:
https://docs.python.org/2/reference/compound_stmts.html#function-definitions
● Avoid mutable default arguments
● If you use mutable default arguments,
○ don’t mutate them
○ have a good reason to mutate them (e.g. caching)
8. Who’s late?
multipliers = [lambda x: (i+1)*x for i in range(10)]
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
9. Who’s late?
multipliers = [lambda x: (i+1)*x for i in range(10)]
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
[20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
10. Who’s late?
multipliers = []
for i in range(10):
def multiplier(x):
return (i+1) * x
multipliers.append(multiplier)
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
11. Who’s late?
multipliers = []
for i in range(10):
def multiplier(x):
return (i+1) * x
multipliers.append(multiplier)
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
[20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
12. ● A closure occurs when a function has access to a local variable from
an enclosing scope that has finished its execution
● Closures in python are late-binding
● This means that the values of variables used in closures are looked
up at the time the closure is called
● This behavior is NOT specific to lambdas
Who’s late?
13. Who’s late?
multipliers = [lambda x, i=i: (i+1)*x for i in range(10)]
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
14. Who’s late?
from functools import partial
from operator import mul
multipliers = [partial(mul, i+1) for i in range(10)]
def print_multiplication_table_for(number):
print [multiplier(number) for multiplier in multipliers]
print_multiplication_table_for(2)
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
18. Western Addition (A)
a = b = [1,2,3]
a += [4]
print a, b
a = a + [5]
print a, b
[1, 2, 3, 4] [1, 2, 3, 4]
[1, 2, 3, 4, 5] [1, 2, 3, 4]
19. ● a + b uses __add__ while a += b uses __iadd__
● An object's __add__ method is regular addition: it takes two
parameters, returns their sum, and doesn't modify either parameter
● An object's __iadd__ method also takes two parameters, but makes
the change in-place, modifying the contents of the first parameter
Western Addition (A)
21. Western Addition (B)
a = b = 'western'
a += 'addition'
print a, b
a = a + 'sf'
print a, b
22. Western Addition (B)
a = b = 'western'
a += 'addition'
print a, b
a = a + 'sf'
print a, b
westernaddition western
westernadditionsf western
23. ● a + b uses __add__ while a += b uses __iadd__, if it exists
● An object's __add__ method is regular addition: it takes two
parameters, returns their sum, and doesn't modify either parameter
● An object's __iadd__ method also takes two parameters, but makes
the change in-place, modifying the contents of the first parameter
● Because this requires object mutation, immutable types (like
Strings!) don’t have an __iadd__ method
Western Addition (B)
26. Western Addition (C)
a = ([1,2,3],)
a[0] += [4]
print a
TypeError: 'tuple' object does
not support item assignment
([1, 2, 3, 4],)
27. ● An object's __iadd__ method takes two parameters, but makes the
change in-place, modifying the contents of the first parameter and
returns the first parameter
● The code above is equivalent to:
Western Addition (C)
a = ([1,2,3],)
x = a[0]
x = x.__iadd__([4]) # returns x itself
a[0] = x
29. Western Addition (D)
a = b = 500
print a is b
a -= 200
b -= 200
print a is b
a = a - 200
b = b - 200
print a is b
--a
--b
print a is b
30. Western Addition (D)
a = b = 500
print a is b
a -= 200
b -= 200
print a is b
a = a - 200
b = b - 200
print a is b
--a
--b
print a is b
True
False
True
True
31. From the python documentation:
https://docs.python.org/2/c-api/int.html
Western Addition (D)
● There is no decrement (--) operator in python
● -- is simply two negation operators
34. Crossing the Boundary (A)
a, b = 0, 1
def fib(n):
for i in range(n):
a, b = b, a + b
return a
print fib(4)
35. Crossing the Boundary (A)
a, b = 0, 1
def fib(n):
for i in range(n):
a, b = b, a + b
return a
print fib(4)
UnboundLocalError: local
variable 'b' referenced before
assignment
36. Crossing the Boundary (A)
x = 1
def f1():
print x
f1()
print x
x = 1
def f2():
x = 2
print x
f2()
print x
x = 1
def f3():
print x
x = 3
f3()
print x
37. Crossing the Boundary (A)
x = 1
def f1():
print x
f1()
print x
x = 1
def f2():
x = 2
print x
f2()
print x
x = 1
def f3():
print x
x = 3
f3()
print x
1
1
2
1
UnboundLocalError: local
variable 'x' referenced
before assignment
1
38. ● If we want to access a global variable from inside a function, it is
possible
● The assignment statement inside a function creates variables in the
local scope
● If a local variable has the same name as a global variable the local
variable will always take precedence
● The assignment inside the function does not modify the global
variable
● We may not refer to both a global variable and a local variable by
the same name inside the same function, it gives us an error
● Deep Dive http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python
Crossing the Boundary (A)
39. Crossing the Boundary (A)
a, b = 0, 1
def fib(n):
for i in range(n):
global a, b
a, b = b, a + b
return a
print fib(4) 3
44. Crossing the Boundary (C)
main.py
try:
import quitter
except:
exit(0)
quitter.quit()
quitter.py
def quit():
exit(0)
# Add one indented line here
# to make main.py crash
python main.py
49. Kids These Days (A)
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x
50. Kids These Days (A)
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x
1 1 1
1 2 1
3 2 3
51. ● In Python, class variables are internally handled as dictionaries.
● If a variable name is not found in the dictionary of the current class,
the class hierarchy (i.e., its parent classes) are searched until the
referenced variable name is found
● If the referenced variable name is not found in the class itself or
anywhere in its hierarchy, an AttributeError occurs
Kids These Days (A)
53. Kids These Days (B)
class Parent:
x = 1
class Child1(Parent):
pass
class Child2(Parent):
x = 2
class GrandChild(Child1, Child2):
pass
print GrandChild.x
54. Kids These Days (B)
class Parent:
x = 1
class Child1(Parent):
pass
class Child2(Parent):
x = 2
class GrandChild(Child1, Child2):
pass
print GrandChild.x 1
55. Kids These Days (B)
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
x = 2
class GrandChild(Child1, Child2):
pass
print GrandChild.x 2
56. ● If you don’t inherit from ‘object’, you’re defining an old style class
● MRO is Method Resolution Order. Lookup __mro__
● MRO for old style classes follows a native DFS approach
Grandchild -> Child 1 -> Parent -> Child 2 -> Parent
● MRO for new style classes is more sensible
Grandchild -> Child 1 -> Child 2 -> Parent
● Don’t use old-style classes
Kids These Days (B)
61. ● If you’re catching multiple exceptions in python 2.x,
you must enclose them in parentheses
● If you do not, the second argument to except is treated as a
reference to the exception object. E.g. this may look familiar:
except Exception, e
● This problem goes away in python 3.x as
except Exception, e
is not a valid syntax. It is replaced by
except Exception as e
Exceptional Circumstances
63. So long!
x = set([type(1), type(1L), type(1.0)])
y = set([1.__class__, 1L.__class__, 1.0.__class__])
print x == y
(a) True
(b) False
(c) TypeError
(d) None of the above
64. So long!
x = set([type(1), type(1L), type(1.0)])
y = set([1.__class__, 1L.__class__, 1.0.__class__])
print x == y
(a) True
(b) False
(c) TypeError
(d) None of the above
y = set([1.__class__, 1L.__class__, 1.0.__class__])
^
SyntaxError: invalid syntax
65. It is a tokenization issue!
The . is parsed as the beginning of the fractional part of a floating point
number. When it encounters something other than a number, it will
throw an error.
So long!
66. So long!
x = set([type(1), type(1L), type(1.0)])
y = set([(1).__class__, 1L.__class__, 1.0.__class__])
print x == y
(a) True
(b) False
(c) TypeError
(d) None of the above
68. Zilch
z = False
i = []
l = 0,
c = None
h = {}
print any((z, i, l, c, h))
(a) True
(b) False
(c) (False, False, False, False, False)
(d) None of the above
69. Zilch
z = False
i = []
l = 0,
c = None
h = {}
print any((z, i, l, c, h))
(a) True
(b) False
(c) (False, False, False, False, False)
(d) None of the above
70. Zilch
z = False
i = []
l = 0,
c = None
h = {}
print any((z, i, l, c, h))
(a) True
(b) False
(c) (False, False, False, False, False)
(d) None of the above
71. When creating a tuple, in all cases except the empty tuple, the comma
is the important thing.
Parentheses are only required for other syntactic reasons: to distinguish
a tuple from a set of function arguments, operator precedence, or to
allow line breaks.
Zilch
https://docs.python.org/2/tutorial/datastructures.html#tuples-and-sequences
80. return in finally will swallow the actual return value in the try block
return in finally will swallow the exception
Finally
81. References
● How to format code within a presentation
● Python Puzzlers by Tendayi Mawushe at PyCon Ireland 2010
● Python Puzzlers by Alan Pierce at Khan Academy
● Python Epiphanies by Stuart Williams at PyCon Montreal 2015
● Python Interview Questions
● Python Gotchas
● Raymon Hettinger’s twitter feed @raymondh