diff --git a/advanced_topics/05_context_managers.ipynb b/advanced_topics/05_context_managers.ipynb index 7f2f3d39058b0026c3ace2c9a18e75f85238200a..ad3c231ca860ce526be06337f65cc74a1e09920d 100644 --- a/advanced_topics/05_context_managers.ipynb +++ b/advanced_topics/05_context_managers.ipynb @@ -35,6 +35,18 @@ "managers_](https://docs.python.org/3.5/reference/datamodel.html#context-managers).\n", "\n", "\n", + "* [Anatomy of a context manager](#anatomy-of-a-context-manager)\n", + " * [Why not just use `try ... finally`?](#why-not-just-use-try-finally)\n", + "* [Uses for context managers](#uses-for-context-managers)\n", + " * [Handling errors in `__exit__`](#handling-errors-in-exit)\n", + " * [Suppressing errors with `__exit__`](#suppressing-errors-with-exit)\n", + "* [Nesting context managers](#nesting-context-managers)\n", + "* [Functions as context managers](#functions-as-context-managers)\n", + "* [Methods as context managers](#methods-as-context-managers)\n", + "* [Useful references](#useful-references)\n", + "\n", + "\n", + "<a class=\"anchor\" id=\"anatomy-of-a-context-manager\"></a>\n", "## Anatomy of a context manager\n", "\n", "\n", @@ -109,6 +121,7 @@ "finalisation logic that we always want to have executed.\n", "\n", "\n", + "<a class=\"anchor\" id=\"why-not-just-use-try-finally\"></a>\n", "### Why not just use `try ... finally`?\n", "\n", "\n", @@ -141,6 +154,7 @@ "logic in one place, and re-use it as many times as you want.\n", "\n", "\n", + "<a class=\"anchor\" id=\"uses-for-context-managers\"></a>\n", "## Uses for context managers\n", "\n", "\n", @@ -232,6 +246,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "<a class=\"anchor\" id=\"handling-errors-in-exit\"></a>\n", "### Handling errors in `__exit__`\n", "\n", "\n", @@ -314,6 +329,7 @@ " number).\n", "\n", "\n", + "<a class=\"anchor\" id=\"suppressing-errors-with-exit\"></a>\n", "### Suppressing errors with `__exit__`\n", "\n", "\n", @@ -376,6 +392,49 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "<a class=\"anchor\" id=\"nesting-context-managers\"></a>\n", + "## Nesting context managers\n", + "\n", + "\n", + "It is possible to nest `with` statements:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('05_context_managers.md', 'rt') as inf:\n", + " with TempDir():\n", + " with open('05_context_managers.md.copy', 'wt') as outf:\n", + " outf.write(inf.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use multiple context managers in a single `with` statement:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('05_context_managers.md', 'rt') as inf, \\\n", + " TempDir(), \\\n", + " open('05_context_managers.md.copy', 'wt') as outf:\n", + " outf.write(inf.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<a class=\"anchor\" id=\"functions-as-context-managers\"></a>\n", "## Functions as context managers\n", "\n", "\n", @@ -401,16 +460,16 @@ "\n", "@contextlib.contextmanager\n", "def tempdir():\n", - " testdir = tempfile.mkdtemp()\n", + " tdir = tempfile.mkdtemp()\n", " prevdir = os.getcwd()\n", " try:\n", "\n", - " os.chdir(testdir)\n", - " yield testdir\n", + " os.chdir(tdir)\n", + " yield tdir\n", "\n", " finally:\n", " os.chdir(prevdir)\n", - " shutil.rmtree(testdir)" + " shutil.rmtree(tdir)" ] }, { @@ -429,7 +488,7 @@ "source": [ "print('In directory: {}'.format(os.getcwd()))\n", "\n", - "with tempdir():\n", + "with tempdir() as tmp:\n", " print('Now in directory: {}'.format(os.getcwd()))\n", "\n", "print('Back in directory: {}'.format(os.getcwd()))" @@ -439,6 +498,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "The `yield tdir` statement in our `tempdir` function causes the `tdir` value\n", + "to be passed to the `with` statement, so in the line `with tempdir() as tmp`,\n", + "the variable `tmp` will be given the value `tdir`.\n", + "\n", + "\n", "> <sup>1</sup> The `yield` keyword is used in _generator functions_.\n", "> Functions which are used with the `@contextmanager` decorator must be\n", "> generator functions which yield exactly one value.\n", @@ -447,6 +511,7 @@ "> beyond the scope of this practical.\n", "\n", "\n", + "<a class=\"anchor\" id=\"methods-as-context-managers\"></a>\n", "## Methods as context managers\n", "\n", "\n", @@ -561,10 +626,6 @@ "source": [ "import matplotlib.pyplot as plt\n", "\n", - "# this line is only necessary when\n", - "# working in jupyer notebook/ipython\n", - "%matplotlib\n", - "\n", "class Plotter(object):\n", " def __init__(self, axis):\n", " self.__axis = axis\n", @@ -608,6 +669,10 @@ "metadata": {}, "outputs": [], "source": [ + "# this line is only necessary when\n", + "# working in jupyer notebook/ipython\n", + "%matplotlib\n", + "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "plotter = Plotter(ax)\n", @@ -740,11 +805,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Nesting context managers\n", - "\n", - "\n", - "\n", - "\n", + "<a class=\"anchor\" id=\"useful-references\"></a>\n", "## Useful references\n", "\n", "\n", diff --git a/advanced_topics/05_context_managers.md b/advanced_topics/05_context_managers.md index 13ff3dd6b173a307e35c7b6ccc49743633167104..2229471e09769cb25159ea203f4e598527b33b8a 100644 --- a/advanced_topics/05_context_managers.md +++ b/advanced_topics/05_context_managers.md @@ -21,6 +21,18 @@ these internals are in fact quite straightforward, and are known as [_context managers_](https://docs.python.org/3.5/reference/datamodel.html#context-managers). +* [Anatomy of a context manager](#anatomy-of-a-context-manager) + * [Why not just use `try ... finally`?](#why-not-just-use-try-finally) +* [Uses for context managers](#uses-for-context-managers) + * [Handling errors in `__exit__`](#handling-errors-in-exit) + * [Suppressing errors with `__exit__`](#suppressing-errors-with-exit) +* [Nesting context managers](#nesting-context-managers) +* [Functions as context managers](#functions-as-context-managers) +* [Methods as context managers](#methods-as-context-managers) +* [Useful references](#useful-references) + + +<a class="anchor" id="anatomy-of-a-context-manager"></a> ## Anatomy of a context manager @@ -71,6 +83,7 @@ This means that we can use context managers to perform any sort of clean up or finalisation logic that we always want to have executed. +<a class="anchor" id="why-not-just-use-try-finally"></a> ### Why not just use `try ... finally`? @@ -95,6 +108,7 @@ But context managers have the advantage that you can implement your clean-up logic in one place, and re-use it as many times as you want. +<a class="anchor" id="uses-for-context-managers"></a> ## Uses for context managers @@ -162,6 +176,7 @@ with TempDir(): ``` +<a class="anchor" id="handling-errors-in-exit"></a> ### Handling errors in `__exit__` @@ -220,6 +235,7 @@ So when an error occurs, the `__exit__` method is passed the following: number). +<a class="anchor" id="suppressing-errors-with-exit"></a> ### Suppressing errors with `__exit__` @@ -265,6 +281,32 @@ with MyContextManager(): ``` +<a class="anchor" id="nesting-context-managers"></a> +## Nesting context managers + + +It is possible to nest `with` statements: + +``` +with open('05_context_managers.md', 'rt') as inf: + with TempDir(): + with open('05_context_managers.md.copy', 'wt') as outf: + outf.write(inf.read()) +``` + + +You can also use multiple context managers in a single `with` statement: + + +``` +with open('05_context_managers.md', 'rt') as inf, \ + TempDir(), \ + open('05_context_managers.md.copy', 'wt') as outf: + outf.write(inf.read()) +``` + + +<a class="anchor" id="functions-as-context-managers"></a> ## Functions as context managers @@ -285,18 +327,19 @@ import contextlib @contextlib.contextmanager def tempdir(): - testdir = tempfile.mkdtemp() + tdir = tempfile.mkdtemp() prevdir = os.getcwd() try: - os.chdir(testdir) - yield testdir + os.chdir(tdir) + yield tdir finally: os.chdir(prevdir) - shutil.rmtree(testdir) + shutil.rmtree(tdir) ``` + This new `tempdir` function is used in exactly the same way as our `TempDir` class: @@ -304,13 +347,18 @@ class: ``` print('In directory: {}'.format(os.getcwd())) -with tempdir(): +with tempdir() as tmp: print('Now in directory: {}'.format(os.getcwd())) print('Back in directory: {}'.format(os.getcwd())) ``` +The `yield tdir` statement in our `tempdir` function causes the `tdir` value +to be passed to the `with` statement, so in the line `with tempdir() as tmp`, +the variable `tmp` will be given the value `tdir`. + + > <sup>1</sup> The `yield` keyword is used in _generator functions_. > Functions which are used with the `@contextmanager` decorator must be > generator functions which yield exactly one value. @@ -319,6 +367,7 @@ print('Back in directory: {}'.format(os.getcwd())) > beyond the scope of this practical. +<a class="anchor" id="methods-as-context-managers"></a> ## Methods as context managers @@ -412,10 +461,6 @@ instances: ``` import matplotlib.pyplot as plt -# this line is only necessary when -# working in jupyer notebook/ipython -%matplotlib - class Plotter(object): def __init__(self, axis): self.__axis = axis @@ -451,6 +496,10 @@ the `matplotlib` plot will open in a separate window): ``` +# this line is only necessary when +# working in jupyer notebook/ipython +%matplotlib + fig = plt.figure() ax = fig.add_subplot(111) plotter = Plotter(ax) @@ -556,11 +605,7 @@ with plotter.holdUpdates(): ``` -## Nesting context managers - - - - +<a class="anchor" id="useful-references"></a> ## Useful references