[ Index | Exercise 5.1 | Exercise 5.3 ]
Objectives:
- Returning values from functions
In this exercise, we briefly look at problems related to returning values from functions. At first glance, it seems like this should be straightforward, but there are some subtle problems that arise.
Suppose you were writing code to parse configuration files consisting of lines like this:
name=value
Write a function parse_line(line)
that takes such a line and returns both the associated name and
value. The common convention for returning multiple values is to return them in a tuple. For example:
>>> parse_line('email=guido@python.org')
('email', 'guido@python.org')
>>> name, val = parse_line('email=guido@python.org')
>>> name
'email'
>>> val
'guido@python.org'
>>>
Sometimes a function might return an optional value--possibly as a mechanism for indicating
success or failure. The most common convention is to use None
as a representation for
a missing value. Modify the parse_line()
function above so that it either returns a tuple
on success or None
on bad data. For example:
>>> parse_line('email=guido@python.org')
('email', 'guido@python.org')
>>> parse_line('spam') # Returns None
>>>
Design discussion: Would it be better for the parse_line()
function to raise an exception
on malformed data?
Sometimes Python code executes concurrently via threads or processes. To illustrate, try this example:
>>> import time
>>> def worker(x, y):
print('About to work')
time.sleep(20)
print('Done')
return x + y
>>> worker(2, 3) # Normal function call
About to work
Done
5
>>>
Now, launch worker()
into a separate thread:
>>> import threading
>>> t = threading.Thread(target=worker, args=(2, 3))
>>> t.start()
About to work
>>>
Done
Carefully notice that the result of the calculation appears nowhere. Not only that, you don't even
know when it's going to be completed. There is a certain coordination problem here. The
convention for handling this case is to wrap the result of a function in a Future
. A
Future
represents a future result. Here's how it works:
>>> from concurrent.futures import Future
>>> # Wrapper around the function to use a future
>>> def do_work(x, y, fut):
fut.set_result(worker(x,y))
>>> fut = Future()
>>> t = threading.Thread(target=do_work, args=(2, 3, fut))
>>> t.start()
About to work
>>> result = fut.result()
Done
>>> result
5
>>>
You'll see this kind of pattern a lot of if working with thread pools, processes, and other constructs. For example:
>>> from concurrent.futures import ThreadPoolExecutor
>>> pool = ThreadPoolExecutor()
>>> fut = pool.submit(worker, 2, 3)
About to work
>>> fut
<Future at 0x102157080 state=running>
>>> fut.result()
Done
5
>>>
[ Solution | Index | Exercise 5.1 | Exercise 5.3 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License