Skip to content

Latest commit

 

History

History
126 lines (99 loc) · 3.36 KB

ex5_2.md

File metadata and controls

126 lines (99 loc) · 3.36 KB

[ Index | Exercise 5.1 | Exercise 5.3 ]

Exercise 5.2

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.

(a) Returning Multiple Values

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'
>>>

(b) Returning Optional Values

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?

(c) Futures

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