{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "In this notebook, we will demonstrate persistent displays for function calls; each time the same function runs the same display will continue to update.\n", "\n", "This is my first foray into doing weird stuff in `IPython` with notebooks. [IPython 7.0](https://blog.jupyter.org/ipython-7-0-async-repl-a35ce050f7f7)\n", "introduces async conventions into the IPython shell. Now it is time to start using them in the notebook. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ " import IPython, asyncio, inspect, time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`f` is a function that returns a __plain/text__ value. In the signature, we assign a default display object that will\n", "persist through out the same session. After the body of the function evaluates we must update the display by hand." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ " async def f(x, display=IPython.display.display(None, display_id=True)):\n", " time.sleep(4)\n", " return display.update(x) or x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calling display in the function signature will attach the output to the function definition cell. And that output is updated\n", "each time the function is executed." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ " assert inspect.iscoroutinefunction(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`asyncio.ensure_future` will run `f` concurrently, but it will not block our notebook session." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] } ], "source": [ " %time task = asyncio.ensure_future(f(10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We time the cell executed above and notice that it occurs instantaneously.\n", "\n", "> This demo uses Python 3.6 so we cannot use [`asyncio.create_task`](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) which is new in Python 3.7." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ " assert inspect.isawaitable(task), \"\"\"A special feature of asyncrohonous function calls that they are awaitable.\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] } ], "source": [ " %time task = asyncio.ensure_future(f(100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, the cell execution instant while the evaluation is occuring the background." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other display types\n", "\n", "It is important to know the display type before creating an output. For example, if we wanted to the perform the same operation \n", "on `pandas` we would have to create an HTML display ahead of time." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ABCD
dQk4N8kDJr0.053310-0.0253880.115704-1.712441
PDAW37TO9p1.016993-0.6868322.188959-0.233486
lfSmzMsnAL-0.5046521.566282-0.029445-0.767817
\n", "
" ], "text/plain": [ " A B C D\n", "dQk4N8kDJr 0.053310 -0.025388 0.115704 -1.712441\n", "PDAW37TO9p 1.016993 -0.686832 2.188959 -0.233486\n", "lfSmzMsnAL -0.504652 1.566282 -0.029445 -0.767817" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ " df = __import__('pandas').util.testing.makeDataFrame()\n", " async def dataframe(rows, display=IPython.display.display(IPython.display.HTML(\" \"), display_id=True)):\n", " value = df.iloc[:rows]\n", " return display.update(value) or df\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Call the `dataframe` function to update it's default display." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] }, { "data": { "text/plain": [ ":2>>" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ " %time asyncio.ensure_future(dataframe(6))" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wall time: 0 ns\n" ] }, { "data": { "text/plain": [ ":2>>" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ " %time asyncio.ensure_future(dataframe(3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Discussion\n", "\n", "Granted this is not a compact or idiomatic notation. It does present a way of thinking about functions having persistant view." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Explore the `IPython.display.display` documentation to understand the variables a little better." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.6" } }, "nbformat": 4, "nbformat_minor": 2 }