In this notebook, we will demonstrate persistent displays for function calls; each time the same function runs the same display will continue to update.
This is my first foray into doing weird stuff in IPython
with notebooks. IPython 7.0
introduces async
conventions into the IPython shell. Now it is time to start using them in the notebook.
import IPython, asyncio, inspect, time
f
is a function that returns a plain/text value. In the signature, we assign a default display object that will
persist through out the same session. After the body of the function evaluates we must update the display by hand.
async def f(x, display=IPython.display.display(None, display_id=True)):
time.sleep(4)
return display.update(x) or x
100
Calling display in the function signature will attach the output to the function definition cell. And that output is updated each time the function is executed.
assert inspect.iscoroutinefunction(f)
asyncio.ensure_future
will run f
concurrently, but it will not block our notebook session.
%time task = asyncio.ensure_future(f(10))
Wall time: 0 ns
We time the cell executed above and notice that it occurs instantaneously.
This demo uses Python 3.6 so we cannot use
asyncio.create_task
which is new in Python 3.7.
assert inspect.isawaitable(task), """A special feature of asyncrohonous function calls that they are awaitable."""
When task finishes the display assigned in f
's signature is updated with the new value eventually. The same output is updated when the function is run again.
%time task = asyncio.ensure_future(f(100))
Wall time: 0 ns
Similarly, the cell execution instant while the evaluation is occuring the background.
It is important to know the display type before creating an output. For example, if we wanted to the perform the same operation
on pandas
we would have to create an HTML display ahead of time.
df = __import__('pandas').util.testing.makeDataFrame()
async def dataframe(rows, display=IPython.display.display(IPython.display.HTML(" "), display_id=True)):
value = df.iloc[:rows]
return display.update(value) or df
A | B | C | D | |
---|---|---|---|---|
dQk4N8kDJr | 0.053310 | -0.025388 | 0.115704 | -1.712441 |
PDAW37TO9p | 1.016993 | -0.686832 | 2.188959 | -0.233486 |
lfSmzMsnAL | -0.504652 | 1.566282 | -0.029445 | -0.767817 |
Call the dataframe
function to update it's default display.
%time asyncio.ensure_future(dataframe(6))
Wall time: 0 ns
<Task pending coro=<dataframe() running at <ipython-input-7-9621cf51351e>:2>>
%time asyncio.ensure_future(dataframe(3))
Wall time: 0 ns
<Task pending coro=<dataframe() running at <ipython-input-7-9621cf51351e>:2>>
Granted this is not a compact or idiomatic notation. It does present a way of thinking about functions having persistant view.
Explore the IPython.display.display
documentation to understand the variables a little better.