Python Puzzlers

51
Python Puzzlers Tendayi Mawushe PyCon Ireland 2010

description

Following a game show format made popular by Joshua Bloch and Neal Gafter's Java Puzzlers this presentation intends to both entertain and inform. Snippets of Python code the whose behaviour is not entirely obvious are shown, the audience will then be asked to pick from a number of options what the behaviour of the program is. The correct and sometimes non-intuitive answer will then be given along with a brief explanation of the idea the puzzle exposes. Only a modest working knowledge of the Python language is required to understand the puzzles, but the puzzles may also entertain the more experienced Python programmer.

Transcript of Python Puzzlers

Page 1: Python Puzzlers

Python Puzzlers

Tendayi MawushePyCon Ireland 2010

Page 2: Python Puzzlers

IntroductionEight Python Puzzles

Short Python program with curious behaviour

What is the output? (multiple choice)

The correct answer given

How to fix the problem (if there was one)

The moral of the story

What will be coveredLanguage and core libraries

Python 2.6 & 3.x (some puzzles apply to 2.6 only)

Page 3: Python Puzzlers

1. Exceptional Circumstances

try: raise NameError('some_name')except TypeError, NameError: print ('caught exception NameError')except Exception: pass

Page 4: Python Puzzlers

1. What is the output?

try: raise NameError('some_name')except TypeError, NameError: print ('caught exception NameError')except Exception: pass

(a) caught exception NameError(b) SyntaxError: invalid syntax(c) <no output>(d) caught exception TypeError

Page 5: Python Puzzlers

1. What is the output?

(a) caught exception NameError(b) SyntaxError: invalid syntax(c) <no output>(d) caught exception TypeError

Page 6: Python Puzzlers

1. A closer look

try: raise NameError('some_name')except TypeError, NameError: print ('caught exception NameError')except Exception: pass

Page 7: Python Puzzlers

1. How do you fix it?

try: raise NameError('some_name')except (TypeError, NameError): print ('caught exception NameError')except Exception: pass

>>>caught exception NameError

Page 8: Python Puzzlers

1. The moral of the story

When catching multiple exceptions in a single except clause you must surround them in parenthesesThis problem is non-existent problem in Python 3.x because the problematic syntax is not permitted:

except SomeException, variable # not valid 3.x syntax

except SomeException as variable

Page 9: Python Puzzlers

2. Final Countdown

seconds = 10for i in range(10): --secondsif seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)

Page 10: Python Puzzlers

2. What is the output?

seconds = 10for i in range(10): --secondsif seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)

(a) ('Wait for it.', 10)(b) -10(c) SyntaxError: invalid syntax(d) ('Happy New Year!', 0)

Page 11: Python Puzzlers

2. What is the output?

(a) ('Wait for it.', 10)(b) -10(c) SyntaxError: invalid syntax(d) ('Happy New Year!', 0)

Page 12: Python Puzzlers

2. A closer look

seconds = 10for i in range(10): --secondsif seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)

Page 13: Python Puzzlers

2. How do you fix it?

seconds = 10for i in range(10): seconds -= 1if seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)

>>>('Happy New Year!', 0)

Page 14: Python Puzzlers

2. The moral of the story

There is no -- or ++ operator in Python to achieve that effect use -= 1 and += 1--seconds is actually the same as -(-seconds)

Page 15: Python Puzzlers

3. Local News

def news(headline): sports = 'Soccer' for story in locals(): print(locals()[story])news('Politics')

Page 16: Python Puzzlers

3. What is the output?

def news(headline): sports = 'Soccer' for story in locals(): print(locals()[story])news('Politics')

(a) Politics Soccer(b) {'sports': 'Soccer'}(c) Soccer(d) RuntimeError: dictionary changed size during iteration

Page 17: Python Puzzlers

3. What is the output?

(a) Politics Soccer(b) {'sports': 'Soccer'}(c) Soccer(d) RuntimeError: dictionary changed size during iteration

Page 18: Python Puzzlers

3. A closer look

def news(headline): sports = 'Soccer' for story in locals(): print(locals()[story])news('Politics')

Page 19: Python Puzzlers

3. How do you fix it?

def news(headline): sports = 'Soccer' stories = locals() for story in stories: print(stories[story])news('Politics')

>>> PoliticsSoccer

Page 20: Python Puzzlers

3. The moral of the story

When locals() is invoked it updates and returns a dictionary representing the current local symbol tableYou should never attempt to update the locals dictionary, however if you need to access it's contents in a loop assign it to another name first

Page 21: Python Puzzlers

4. TGIF

days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']weekend = enumerate(days)[5:]for day in weekend: print(day[0], day[1])

Page 22: Python Puzzlers

4. What is the output?

days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']weekend = enumerate(days)[5:]for day in weekend: print(day[0], day[1])

(a) (5, 'Sat') (6, 'Sun')(b) ('Sat', 'Sun')(c) TypeError: object is unsubscriptable(d) (5, 6)

Page 23: Python Puzzlers

4. What is the output?

(a) (5, 'Sat') (6, 'Sun')(b) ('Sat', 'Sun')(c) TypeError: object is unsubscriptable(d) (5, 6)

Page 24: Python Puzzlers

4. A closer look

days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']weekend = enumerate(days)[5:]for day in weekend: print(day[0], day[1])

Page 25: Python Puzzlers

4. How do you fix it?

days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']weekend = list(enumerate(days))[5:]for day in weekend: print(day[0], day[1])

>>>(5, 'Sat')(6, 'Sun')

Page 26: Python Puzzlers

4. The moral of the story

The enumerate built-in function is a generator, that is it returns an iteratorIterators are not sequences therefore they cannot be indexed or sliced:

If you need to index or slice an iterator you must first convert it to a list, this loads the entire dataset into memory

Generators can represent infinite chain of values for example itertools.count(), these cannot be meaningfully sliced in reverse

Page 27: Python Puzzlers

5. Rabbits everywhere

a, b = 0, 1def fibonacci(n): for i in range(n): a, b = b, a + b return afib8 = fibonacci(8)print(fib8)

Page 28: Python Puzzlers

5. What is the output?

a, b = 0, 1def fibonacci(n): for i in range(n): a, b = b, a + b return afib8 = fibonacci(8)print(fib8)

(a) UnboundLocalError: local variable(b) 21(c) 1(d) 0

Page 29: Python Puzzlers

5. What is the output?

(a) UnboundLocalError: local variable(b) 21(c) 1(d) 0

Page 30: Python Puzzlers

5. A closer look

a, b = 0, 1def fibonacci(n): for i in range(n): a, b = b, a + b return afib8 = fibonacci(8)print(fib8)

Page 31: Python Puzzlers

5. How do you fix it?

a, b = 0, 1def fibonacci(n): global a, b for i in range(n): a, b = b, a + b return afib8 = fibonacci(8)print(fib8)

>>>21

Page 32: Python Puzzlers

5. The moral of the story

The issue is local variable optimisation.If a variable is assigned in a function it is a local variable, the bytecode generated to access it is different to that for global variables.

A variable in a function can either be local or global, but not both.

Do not mix up global and local names in this way, it is confusing and problematic.

Page 33: Python Puzzlers

6. The Whole Truth

w = Falseh = []o = 0,l = Nonee = {}print(any((w, h, o, l, e)))

Page 34: Python Puzzlers

6. What is the output?

w = Falseh = []o = 0,l = Nonee = {}print(any((w, h, o, l, e)))

(a) True(b) (w, h, o, l, e)(c) (False, [], 0, None, {})(d) False

Page 35: Python Puzzlers

6. What is the output?

(a) True(b) (w, h, o, l, e)(c) (False, [], 0, None, {})(d) False

Page 36: Python Puzzlers

6. A closer look

w = Falseh = []

O = 0,l = Nonee = {}print(any((w, h, o, l, e)))

Page 37: Python Puzzlers

6. How do you fix it?

w = Falseh = []O = 0l = Nonee = {}print(any((w, h, o, l, e)))

>>> False

Page 38: Python Puzzlers

6. The moral of the story

The comma is the tuple constructor, not the parenthesesThough it is not required it is generally considered good style to use parentheses when creating a tuple:

(0,) is better than 0,

Page 39: Python Puzzlers

7. Double or Nothing

def double(items, doubles=[]): for item in items: doubles.append(item * 2) return doublesnumbers = double([1, 2, 3])words = double(['one', 'two', 'three'])print(words)

Page 40: Python Puzzlers

7. What is the output?

def double(items, doubles=[]): for item in items: doubles.append(item * 2) return doublesnumbers = double([1, 2, 3])words = double(['one', 'two', 'three'])print(words)

(a) [2, 4, 6, 'oneone', 'twotwo', 'threethree'](b) ['oneone', 'twotwo', 'threethree'](c) TypeError: unsupported operand type(s) for *(d) [2, 4, 6]

Page 41: Python Puzzlers

7. What is the output?

(a) [2, 4, 6, 'oneone', 'twotwo', 'threethree'](b) ['oneone', 'twotwo', 'threethree'](c) TypeError: unsupported operand type(s) for *(d) [2, 4, 6]

Page 42: Python Puzzlers

7. A closer look

def double(items, doubles=[]): for item in items: doubles.append(item * 2) return doublesnumbers = double([1, 2, 3])words = double(['one', 'two', 'three'])print(words)

Page 43: Python Puzzlers

7. How do you fix it?

def double(items, doubles=None): if doubles is None: doubles = [] for item in items: doubles.append(item * 2) return doublesnumbers = double([1, 2, 3])words = double(['one', 'two', 'three'])print(words)

>>> ['oneone', 'twotwo', 'threethree']

Page 44: Python Puzzlers

7. The moral of the story

Do not use mutable types as default argumentsDefault arguments are evaluated when the function is defined not when the function is called

If you want to use a mutable type as a default argument, set the default to None and initialise it properly inside the function

Page 45: Python Puzzlers

8. Evening Out the Odds

nums = [01, 02, 03, 04, 05, 06, 07, 08, 09, 10]evens = []for num in nums: if num % 2 != 0: # is the number odd evens.append(num + 1)print(evens)

Page 46: Python Puzzlers

8. What is the output?

nums = [01, 02, 03, 04, 05, 06, 07, 08, 09, 10]evens = []for num in nums: if num % 2 != 0: # is the number odd evens.append(num + 1)print(evens)

(a) [2, 4, 6, 8, 10](b) SyntaxError: invalid token(c) [02, 04, 06, 08, 10](d) [2, 2, 4, 4, 6, 6, 8, 8, 10, 10]

Page 47: Python Puzzlers

8. What is the output?

(a) [2, 4, 6, 8, 10](b) SyntaxError: invalid token(c) [02, 04, 06, 08, 10](d) [1, 2, 3, 4, 5]

Page 48: Python Puzzlers

8. A closer look

nums = [01, 02, 03, 04, 05, 06, 07, 08, 09, 10]evens = []for num in nums: if num % 2 != 0: # is the number odd evens.append(num + 1)print(evens)

Page 49: Python Puzzlers

8. How do you fix it?

nums = [01, 02, 03, 04, 05, 06, 07, 010, 011, 012]evens = []for num in nums: if num % 2 != 0: # is the number odd evens.append(oct(num + 1))print(evens)

>>> ['02', '04', '06', '010', '012']

Page 50: Python Puzzlers

8. The moral of the story

In Python 2.x a leading 0 specifies an octal literalIf you want to work with octal numbers remember the valid digits are 0 though 7In Python 3.x octal literals are specified using 0o, which removes the ambiguity

01 # not valid 3.x syntax

0o1

Page 51: Python Puzzlers

Links

Slides: insmallportions.com

Q & A: stackoverflow.comInspiration: javapuzzlers.com