127 lines
3.4 KiB
Markdown
127 lines
3.4 KiB
Markdown
|
\[ [Index](index.md) | [Exercise 5.1](ex5_1.md) | [Exercise 5.3](ex5_3.md) \]
|
||
|
|
||
|
# 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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:
|
||
|
|
||
|
```python
|
||
|
>>> 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](soln5_2.md) | [Index](index.md) | [Exercise 5.1](ex5_1.md) | [Exercise 5.3](ex5_3.md) \]
|
||
|
|
||
|
----
|
||
|
`>>>` Advanced Python Mastery
|
||
|
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||
|
`...` Copyright 2007-2023
|
||
|
|
||
|
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|