Python Puzzlers - 2016 Edition

81
Python Puzzlers Feb 17, 2016 Nandan Sawant, Ryan Rueth

Transcript of Python Puzzlers - 2016 Edition

Page 1: Python Puzzlers - 2016 Edition

Python PuzzlersFeb 17, 2016

Nandan Sawant, Ryan Rueth

Page 2: Python Puzzlers - 2016 Edition

1. Defaulter

Page 3: Python Puzzlers - 2016 Edition

Defaulter

def extend_list(val, l=[]):

l.append(val)

return l

list1 = extend_list(10)

list2 = extend_list(123,[])

list3 = extend_list('a')

print "list1 = %s" % list1

print "list2 = %s" % list2

print "list3 = %s" % list3

Page 4: Python Puzzlers - 2016 Edition

Defaulter

def extend_list(val, l=[]):

l.append(val)

return l

list1 = extend_list(10)

list2 = extend_list(123,[])

list3 = extend_list('a')

print "list1 = %s" % list1

print "list2 = %s" % list2

print "list3 = %s" % list3

list1 = [10, 'a']

list2 = [123]

list3 = [10, 'a']

Page 5: Python Puzzlers - 2016 Edition

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)

Page 6: Python Puzzlers - 2016 Edition

Defaulter

def extend_list(val, l=None):

if l is None:

l = []

l.append(val)

return l

list1 = extend_list(10)

list2 = extend_list(123,[])

list3 = extend_list('a')

print "list1 = %s" % list1

print "list2 = %s" % list2

print "list3 = %s" % list3

list1 = [10]

list2 = [123]

list3 = ['a']

Page 7: Python Puzzlers - 2016 Edition

2. Who’s late?

Page 8: Python Puzzlers - 2016 Edition

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)

Page 9: Python Puzzlers - 2016 Edition

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]

Page 10: Python Puzzlers - 2016 Edition

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)

Page 11: Python Puzzlers - 2016 Edition

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]

Page 12: Python Puzzlers - 2016 Edition

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

Page 13: Python Puzzlers - 2016 Edition

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]

Page 14: Python Puzzlers - 2016 Edition

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]

Page 15: Python Puzzlers - 2016 Edition

3. Western Addition

Page 16: Python Puzzlers - 2016 Edition

Western Addition (A)

Page 17: Python Puzzlers - 2016 Edition

Western Addition (A)

a = b = [1,2,3]

a += [4]

print a, b

a = a + [5]

print a, b

Page 18: Python Puzzlers - 2016 Edition

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]

Page 19: Python Puzzlers - 2016 Edition

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

Page 20: Python Puzzlers - 2016 Edition

Western Addition (B)

Page 21: Python Puzzlers - 2016 Edition

Western Addition (B)

a = b = 'western'

a += 'addition'

print a, b

a = a + 'sf'

print a, b

Page 22: Python Puzzlers - 2016 Edition

Western Addition (B)

a = b = 'western'

a += 'addition'

print a, b

a = a + 'sf'

print a, b

westernaddition western

westernadditionsf western

Page 23: Python Puzzlers - 2016 Edition

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

Page 24: Python Puzzlers - 2016 Edition

Western Addition (C)

Page 25: Python Puzzlers - 2016 Edition

Western Addition (C)

a = ([1,2,3],)

a[0] += [4]

print a

Page 26: Python Puzzlers - 2016 Edition

Western Addition (C)

a = ([1,2,3],)

a[0] += [4]

print a

TypeError: 'tuple' object does

not support item assignment

([1, 2, 3, 4],)

Page 27: Python Puzzlers - 2016 Edition

● 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

Page 28: Python Puzzlers - 2016 Edition

Western Addition (D)

Page 29: Python Puzzlers - 2016 Edition

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

Page 30: Python Puzzlers - 2016 Edition

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

Page 31: Python Puzzlers - 2016 Edition

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

Page 32: Python Puzzlers - 2016 Edition

4. Crossing the Boundary

Page 33: Python Puzzlers - 2016 Edition

Crossing the Boundary (A)

Page 34: Python Puzzlers - 2016 Edition

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)

Page 35: Python Puzzlers - 2016 Edition

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

Page 36: Python Puzzlers - 2016 Edition

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

Page 37: Python Puzzlers - 2016 Edition

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

Page 38: Python Puzzlers - 2016 Edition

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

Page 39: Python Puzzlers - 2016 Edition

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

Page 40: Python Puzzlers - 2016 Edition

Crossing the Boundary (B)

Page 41: Python Puzzlers - 2016 Edition

Crossing the Boundary (B)

Page 42: Python Puzzlers - 2016 Edition

Crossing the Boundary (B)

Page 43: Python Puzzlers - 2016 Edition

Crossing the Boundary (C)

Page 44: Python Puzzlers - 2016 Edition

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

Page 45: Python Puzzlers - 2016 Edition

Crossing the Boundary (C)

main.py

try:

import quitter

except:

exit(0)

quitter.quit()

quitter.py

def quit():

exit(0)

exit = None

python main.py

Page 46: Python Puzzlers - 2016 Edition

Crossing the Boundary (C)

main.py

try:

import quitter

except:

exit(0)

quitter.quit()

quitter.py

def quit():

exit(0)

exit = None

UnboundLocalError: local variable

'exit' referenced before assignment

python main.py

Page 47: Python Puzzlers - 2016 Edition

5. Kids These Days

Page 48: Python Puzzlers - 2016 Edition

Kids These Days (A)

Page 49: Python Puzzlers - 2016 Edition

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

Page 50: Python Puzzlers - 2016 Edition

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

Page 51: Python Puzzlers - 2016 Edition

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

Page 52: Python Puzzlers - 2016 Edition

Kids These Days (B)

Page 53: Python Puzzlers - 2016 Edition

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

Page 54: Python Puzzlers - 2016 Edition

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

Page 55: Python Puzzlers - 2016 Edition

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

Page 56: Python Puzzlers - 2016 Edition

● 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 approachGrandchild -> Child 1 -> Parent -> Child 2 -> Parent

● MRO for new style classes is more sensibleGrandchild -> Child 1 -> Child 2 -> Parent

● Don’t use old-style classes

Kids These Days (B)

Page 57: Python Puzzlers - 2016 Edition

6. Exceptional Circumstances

Page 58: Python Puzzlers - 2016 Edition

Exceptional Circumstances

user_id = 'a'

try:

user_id = int(user_id)

print user_id

except TypeError, ValueError:

print "Oops!"

(a) a(b) 65(c) Oops!(d) raises TypeError(e) raises ValueError

Page 59: Python Puzzlers - 2016 Edition

Exceptional Circumstances

user_id = 'a'

try:

user_id = int(user_id)

print user_id

except TypeError, ValueError:

print "Oops!"

(a) a(b) 65(c) Oops!(d) raises TypeError(e) raises ValueError

ValueError: invalid literal

for int() with base 10: 'a'

Page 60: Python Puzzlers - 2016 Edition

Exceptional Circumstances

user_id = 'a'

try:

user_id = int(user_id)

print user_id

except (TypeError, ValueError):

print "Oops!"

(a) a(b) 65(c) Oops!(d) raises TypeError(e) raises ValueError

Page 61: Python Puzzlers - 2016 Edition

● 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 byexcept Exception as e

Exceptional Circumstances

Page 62: Python Puzzlers - 2016 Edition

7. So long!

Page 63: Python Puzzlers - 2016 Edition

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

Page 64: Python Puzzlers - 2016 Edition

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

Page 65: Python Puzzlers - 2016 Edition

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!

Page 66: Python Puzzlers - 2016 Edition

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

Page 67: Python Puzzlers - 2016 Edition

8. Zilch

Page 68: Python Puzzlers - 2016 Edition

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

Page 69: Python Puzzlers - 2016 Edition

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

Page 70: Python Puzzlers - 2016 Edition

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

Page 71: Python Puzzlers - 2016 Edition

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

Page 72: Python Puzzlers - 2016 Edition

9. It’s 2016!

Page 73: Python Puzzlers - 2016 Edition

It’s 2016!

print 2,016 * 2,016(a) 4064256(b) 4,064,256(c) 4 256(d) 2 32 16(e) None of the above

Page 74: Python Puzzlers - 2016 Edition

It’s 2016!

print 2,016 * 2,016(a) 4064256(b) 4,064,256(c) 4 256(d) 2 32 16(e) None of the above

2 28 14

Page 75: Python Puzzlers - 2016 Edition

The 0 is an outdated prefix that Python 2.x uses to represent Octal numbers.

In Python 3, you must write: 0o16 instead.

It’s 2016!

Page 76: Python Puzzlers - 2016 Edition

10. Finally

Page 77: Python Puzzlers - 2016 Edition

Finally

def find_inverse(n):

inverse = 1

try:

inverse = 1 / k

finally:

return inverse

fi = find_inverse

print fi(-1) + fi(0) + fi(1)

(a) 0(b) 1(c) 3(d) raises ZeroDivisionError(e) raises NameError(f) None of the above

Page 78: Python Puzzlers - 2016 Edition

Finally

def find_inverse(n):

inverse = 1

try:

inverse = 1 / k

finally:

return inverse

fi = find_inverse

print fi(-1) + fi(0) + fi(1)

(a) 0(b) 1(c) 3(d) raises ZeroDivisionError(e) raises NameError(f) None of the above

Page 79: Python Puzzlers - 2016 Edition

Finally

Page 80: Python Puzzlers - 2016 Edition

return in finally will swallow the actual return value in the try block

return in finally will swallow the exception

Finally