Python Puzzlers
Tendayi MawushePyCon Ireland 2010
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)
1. Exceptional Circumstances
try: raise NameError('some_name')except TypeError, NameError: print ('caught exception NameError')except Exception: pass
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
1. What is the output?
(a) caught exception NameError(b) SyntaxError: invalid syntax(c) <no output>(d) caught exception TypeError
1. A closer look
try: raise NameError('some_name')except TypeError, NameError: print ('caught exception NameError')except Exception: pass
1. How do you fix it?
try: raise NameError('some_name')except (TypeError, NameError): print ('caught exception NameError')except Exception: pass
>>>caught exception NameError
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
2. Final Countdown
seconds = 10for i in range(10): --secondsif seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)
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)
2. What is the output?
(a) ('Wait for it.', 10)(b) -10(c) SyntaxError: invalid syntax(d) ('Happy New Year!', 0)
2. A closer look
seconds = 10for i in range(10): --secondsif seconds: print('Wait for it.', seconds)else: print('Happy New Year!', seconds)
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)
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)
3. Local News
def news(headline): sports = 'Soccer' for story in locals(): print(locals()[story])news('Politics')
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
3. What is the output?
(a) Politics Soccer(b) {'sports': 'Soccer'}(c) Soccer(d) RuntimeError: dictionary changed size during iteration
3. A closer look
def news(headline): sports = 'Soccer' for story in locals(): print(locals()[story])news('Politics')
3. How do you fix it?
def news(headline): sports = 'Soccer' stories = locals() for story in stories: print(stories[story])news('Politics')
>>> PoliticsSoccer
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
4. TGIF
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']weekend = enumerate(days)[5:]for day in weekend: print(day[0], day[1])
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)
4. What is the output?
(a) (5, 'Sat') (6, 'Sun')(b) ('Sat', 'Sun')(c) TypeError: object is unsubscriptable(d) (5, 6)
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])
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')
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
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)
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
5. What is the output?
(a) UnboundLocalError: local variable(b) 21(c) 1(d) 0
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)
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
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.
6. The Whole Truth
w = Falseh = []o = 0,l = Nonee = {}print(any((w, h, o, l, e)))
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
6. What is the output?
(a) True(b) (w, h, o, l, e)(c) (False, [], 0, None, {})(d) False
6. A closer look
w = Falseh = []
O = 0,l = Nonee = {}print(any((w, h, o, l, e)))
6. How do you fix it?
w = Falseh = []O = 0l = Nonee = {}print(any((w, h, o, l, e)))
>>> False
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,
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)
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]
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]
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)
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']
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
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)
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]
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]
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)
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']
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
Links
Slides: insmallportions.com
Q & A: stackoverflow.comInspiration: javapuzzlers.com
Top Related