Created
February 8, 2021 20:12
-
-
Save francois-durand/f75845430b2d973cc91ff20549fe2c54 to your computer and use it in GitHub Desktop.
Fastcore_presentation.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "# Fastcore" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "François Durand" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Python Workshop of the Lincs, 10 February 2021" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "skip" | |
}, | |
"toc": true | |
}, | |
"cell_type": "markdown", | |
"source": "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Introduction\" data-toc-modified-id=\"Introduction-1\"><span class=\"toc-item-num\">1 </span>Introduction</a></span></li><li><span><a href=\"#Tools-for-functions\" data-toc-modified-id=\"Tools-for-functions-2\"><span class=\"toc-item-num\">2 </span>Tools for functions</a></span><ul class=\"toc-item\"><li><span><a href=\"#Partial-function:-partialler\" data-toc-modified-id=\"Partial-function:-partialler-2.1\"><span class=\"toc-item-num\">2.1 </span>Partial function: <code>partialler</code></a></span></li><li><span><a href=\"#Composition:-compose\" data-toc-modified-id=\"Composition:-compose-2.2\"><span class=\"toc-item-num\">2.2 </span>Composition: <code>compose</code></a></span></li><li><span><a href=\"#Mapping-with-composition:-maps\" data-toc-modified-id=\"Mapping-with-composition:-maps-2.3\"><span class=\"toc-item-num\">2.3 </span>Mapping with composition: <code>maps</code></a></span></li><li><span><a href=\"#Flexible-mapping:-mapped\" data-toc-modified-id=\"Flexible-mapping:-mapped-2.4\"><span class=\"toc-item-num\">2.4 </span>Flexible mapping: <code>mapped</code></a></span></li><li><span><a href=\"#Type-dispatch-/-multiple-dispatch:-typedispatch\" data-toc-modified-id=\"Type-dispatch-/-multiple-dispatch:-typedispatch-2.5\"><span class=\"toc-item-num\">2.5 </span>Type dispatch / multiple dispatch: <code>typedispatch</code></a></span></li></ul></li><li><span><a href=\"#Tools-for-defining-classes\" data-toc-modified-id=\"Tools-for-defining-classes-3\"><span class=\"toc-item-num\">3 </span>Tools for defining classes</a></span><ul class=\"toc-item\"><li><span><a href=\"#Store-parameters-as-attributes:-store_attr\" data-toc-modified-id=\"Store-parameters-as-attributes:-store_attr-3.1\"><span class=\"toc-item-num\">3.1 </span>Store parameters as attributes: <code>store_attr</code></a></span></li><li><span><a href=\"#Basic-representation-function:-basic_repr\" data-toc-modified-id=\"Basic-representation-function:-basic_repr-3.2\"><span class=\"toc-item-num\">3.2 </span>Basic representation function: <code>basic_repr</code></a></span></li><li><span><a href=\"#Optional-pre/post-init:-PrePostInitMeta\" data-toc-modified-id=\"Optional-pre/post-init:-PrePostInitMeta-3.3\"><span class=\"toc-item-num\">3.3 </span>Optional pre/post-init: <code>PrePostInitMeta</code></a></span></li><li><span><a href=\"#Create-a-class-on-the-fly:-mk_class\" data-toc-modified-id=\"Create-a-class-on-the-fly:-mk_class-3.4\"><span class=\"toc-item-num\">3.4 </span>Create a class on-the-fly: <code>mk_class</code></a></span></li><li><span><a href=\"#Wrap-a-function-into-a-class:-wrap_class\" data-toc-modified-id=\"Wrap-a-function-into-a-class:-wrap_class-3.5\"><span class=\"toc-item-num\">3.5 </span>Wrap a function into a class: <code>wrap_class</code></a></span></li></ul></li><li><span><a href=\"#Classes-and-Functions\" data-toc-modified-id=\"Classes-and-Functions-4\"><span class=\"toc-item-num\">4 </span>Classes and Functions</a></span><ul class=\"toc-item\"><li><span><a href=\"#Monkey-patching:-patch\" data-toc-modified-id=\"Monkey-patching:-patch-4.1\"><span class=\"toc-item-num\">4.1 </span>Monkey patching: <code>patch</code></a></span></li><li><span><a href=\"#Improved-pathlib.Path\" data-toc-modified-id=\"Improved-pathlib.Path-4.2\"><span class=\"toc-item-num\">4.2 </span>Improved <code>pathlib.Path</code></a></span></li><li><span><a href=\"#More-concise-lambdas:-Self\" data-toc-modified-id=\"More-concise-lambdas:-Self-4.3\"><span class=\"toc-item-num\">4.3 </span>More concise lambdas: <code>Self</code></a></span></li><li><span><a href=\"#Function-applying-on-a-particular-attribute:-using_attr\" data-toc-modified-id=\"Function-applying-on-a-particular-attribute:-using_attr-4.4\"><span class=\"toc-item-num\">4.4 </span>Function applying on a particular attribute: <code>using_attr</code></a></span></li></ul></li><li><span><a href=\"#Delegation\" data-toc-modified-id=\"Delegation-5\"><span class=\"toc-item-num\">5 </span>Delegation</a></span><ul class=\"toc-item\"><li><span><a href=\"#Delegated-inheritance:-delegates\" data-toc-modified-id=\"Delegated-inheritance:-delegates-5.1\"><span class=\"toc-item-num\">5.1 </span>Delegated inheritance: <code>delegates</code></a></span></li><li><span><a href=\"#Delegated-composition:-GetAttr\" data-toc-modified-id=\"Delegated-composition:-GetAttr-5.2\"><span class=\"toc-item-num\">5.2 </span>Delegated composition: <code>GetAttr</code></a></span></li></ul></li><li><span><a href=\"#Misc\" data-toc-modified-id=\"Misc-6\"><span class=\"toc-item-num\">6 </span>Misc</a></span><ul class=\"toc-item\"><li><span><a href=\"#Class-L-for-lists\" data-toc-modified-id=\"Class-L-for-lists-6.1\"><span class=\"toc-item-num\">6.1 </span>Class <code>L</code> for lists</a></span></li><li><span><a href=\"#Group-items:-groupby\" data-toc-modified-id=\"Group-items:-groupby-6.2\"><span class=\"toc-item-num\">6.2 </span>Group items: <code>groupby</code></a></span></li><li><span><a href=\"#Simple-parallel-processing:-parallel\" data-toc-modified-id=\"Simple-parallel-processing:-parallel-6.3\"><span class=\"toc-item-num\">6.3 </span>Simple parallel processing: <code>parallel</code></a></span></li><li><span><a href=\"#Notebook-Functions:-in_notebook,-in_colab,-in_ipython\" data-toc-modified-id=\"Notebook-Functions:-in_notebook,-in_colab,-in_ipython-6.4\"><span class=\"toc-item-num\">6.4 </span>Notebook Functions: <code>in_notebook</code>, <code>in_colab</code>, <code>in_ipython</code></a></span></li><li><span><a href=\"#Other-features\" data-toc-modified-id=\"Other-features-6.5\"><span class=\"toc-item-num\">6.5 </span>Other features</a></span></li></ul></li></ul></div>" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.288935Z", | |
"start_time": "2021-02-08T19:59:35.426590Z" | |
}, | |
"slideshow": { | |
"slide_type": "skip" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import time\nimport itertools\nimport functools\nimport numpy as np\nimport pandas as pd\nfrom fastcore.basics import basic_repr, compose, ge, groupby, itemgetter, \\\n maps, mk_class, partialler, patch, Self, store_attr, using_attr, wrap_class\nfrom fastcore.dispatch import GetAttr, typedispatch\nfrom fastcore.foundation import L\nfrom fastcore.imports import in_notebook, in_colab, in_ipython\nfrom fastcore.meta import delegates, PrePostInitMeta, use_kwargs\nfrom fastcore.parallel import parallel\nfrom fastcore.utils import mapped", | |
"execution_count": 1, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Introduction" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "https://github.com/fastai/fastcore\n\nhttps://fastpages.fast.ai/fastcore/\n\nhttps://www.fast.ai/2019/08/06/delegation/" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Disclaimer: for the moment, I don't use fastcore in my projects. I just saw it in some news and thought it was worth having a look. So I share!" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "From the website, Fastcore is defined as:\n\nPython goodies to make your coding faster, easier, and more maintainable" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "\"Python is a powerful, dynamic language. Rather than bake everything into the language, it lets the programmer customize it to make it work for them. fastcore uses this flexibility to **add to Python features inspired by other languages we've loved**, like multiple dispatch from Julia, mixins from Ruby, and currying, binding, and more from Haskell. It also adds some **\"missing features\"** and **clean up some rough edges** in the Python standard library, such as simplifying parallel processing, and bringing ideas from NumPy over to Python's list type.\"" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Tools for functions" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Partial function: ``partialler``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.298624Z", | |
"start_time": "2021-02-08T19:59:38.289933Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def my_filter(arr, val): \n \"Filter a list to keep only values >= val.\"\n return [x for x in arr if x >= val]\n\nmy_filter([1, 2, 3, 4, 5, 6], 5)", | |
"execution_count": 2, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "[5, 6]" | |
}, | |
"execution_count": 2, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.318857Z", | |
"start_time": "2021-02-08T19:59:38.301901Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "filter5 = functools.partial(my_filter, val=5)\nfilter5([1, 2, 3, 4, 5, 6])", | |
"execution_count": 3, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "[5, 6]" | |
}, | |
"execution_count": 3, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.333816Z", | |
"start_time": "2021-02-08T19:59:38.321848Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "filter5.__doc__", | |
"execution_count": 4, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'partial(func, *args, **keywords) - new function with partial application\\n of the given arguments and keywords.\\n'" | |
}, | |
"execution_count": 4, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ The documentation is not very helpful!" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``partialler``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.350771Z", | |
"start_time": "2021-02-08T19:59:38.336810Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "filter5 = partialler(my_filter, val=5)\nfilter5([1, 2, 3, 4, 5, 6])", | |
"execution_count": 5, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "[5, 6]" | |
}, | |
"execution_count": 5, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.364770Z", | |
"start_time": "2021-02-08T19:59:38.352765Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "filter5.__doc__", | |
"execution_count": 6, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Filter a list to keep only values >= val.'" | |
}, | |
"execution_count": 6, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Composition: ``compose``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Basic (but not very motivating) example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.383684Z", | |
"start_time": "2021-02-08T19:59:38.366729Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def my_filter(arr, val): \n return [x for x in arr if x >= val]\n\ndef add(arr, val):\n return [x + val for x in arr]\n\ndef arrsum(arr):\n return sum(arr)\n\ndef transform(arr):\n x = my_filter(arr, 5)\n x = add(x, 2)\n x = arrsum(x)\n return x # Or directly: return arrsum(add(my_filter(arr, 5), 2))\n\ntransform([1, 2, 3, 4, 5, 6])", | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "15" | |
}, | |
"execution_count": 7, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``compose`` (and ``partialler``):" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.400638Z", | |
"start_time": "2021-02-08T19:59:38.388829Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def my_filter(arr, val): \n return [x for x in arr if x >= val]\n\ndef add(arr, val):\n return [x + val for x in arr]\n\ndef arrsum(arr):\n return sum(arr)\n\ntransform = compose(partialler(my_filter, val=5), partialler(add, val=2), arrsum)\n\ntransform([1, 2, 3, 4, 5, 6])", | |
"execution_count": 8, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "15" | |
}, | |
"execution_count": 8, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ More readable than ``arrsum(add(my_filter(arr, 5), 2))``." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Real motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.419608Z", | |
"start_time": "2021-02-08T19:59:38.403682Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def transform_and_mean(x, transforms: list):\n \"fit a model after performing transformations\"\n x_transformed = compose(*transforms)(x)\n return np.mean(x_transformed)\n\n# Filter out elements < 5, add 2, then predict the mean\ntransform_and_mean(x=[1, 2, 3, 4, 5, 6], transforms=[partialler(my_filter, val=5), partialler(add, val=2)])", | |
"execution_count": 9, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "7.5" | |
}, | |
"execution_count": 9, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Mapping with composition: ``maps``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T15:02:56.169859Z", | |
"start_time": "2021-02-08T15:02:56.163938Z" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Like ``map``, but functions are composed first." | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.439567Z", | |
"start_time": "2021-02-08T19:59:38.422625Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def add_1(x):\n return x + 1\n\ndef square(x):\n return x**2\n\nfor x in maps(add_1, square, [1, 2]):\n print(x)", | |
"execution_count": 10, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "4\n9\n" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Flexible mapping: ``mapped``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Like ``map``, but return ``f(x)`` if ``x`` is not \"listy\":" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.454527Z", | |
"start_time": "2021-02-08T19:59:38.442551Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def add_1(x):\n return x + 1", | |
"execution_count": 11, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.471447Z", | |
"start_time": "2021-02-08T19:59:38.456542Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "mapped(add_1, 1)", | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "2" | |
}, | |
"execution_count": 12, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.489400Z", | |
"start_time": "2021-02-08T19:59:38.473442Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "mapped(add_1, [1, 2])", | |
"execution_count": 13, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#2) [2,3]" | |
}, | |
"execution_count": 13, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Type dispatch / multiple dispatch: ``typedispatch``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Type dispatch, or multiple dispatch, allows you to change the way a function behaves based upon the input types it receives (like e.g. in Julia):\n\n* ``f(x: str, y: str) = something``,\n* ``f(x: int, y: int) = something else``,\n* ``f(x: array) = something else``." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``@typedispatch``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.505399Z", | |
"start_time": "2021-02-08T19:59:38.491394Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "@typedispatch\ndef f(x: str, y: str):\n return f'{x} {y}'\n\n@typedispatch\ndef f(x: int, y: int):\n return x + y\n\n@typedispatch\ndef f(x: np.ndarray):\n return x.sum()", | |
"execution_count": 14, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.525304Z", | |
"start_time": "2021-02-08T19:59:38.507352Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f('Hello', 'World!')", | |
"execution_count": 15, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Hello World!'" | |
}, | |
"execution_count": 15, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.544341Z", | |
"start_time": "2021-02-08T19:59:38.527298Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f(2, 3)", | |
"execution_count": 16, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "5" | |
}, | |
"execution_count": 16, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.561209Z", | |
"start_time": "2021-02-08T19:59:38.546251Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f(np.array([1, 2, 3, 4]))", | |
"execution_count": 17, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "10" | |
}, | |
"execution_count": 17, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Tools for defining classes" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Store parameters as attributes: ``store_attr``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.580321Z", | |
"start_time": "2021-02-08T19:59:38.564199Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a, b ,c): \n self.a = a\n self.b = b\n self.c = c\n \nfoo = Foo(5, 4, 3)\nfoo.a, foo.b, foo.c", | |
"execution_count": 18, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(5, 4, 3)" | |
}, | |
"execution_count": 18, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ A lot of boilerplate code!" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Remark: if you want a class that does only this, you can use the standard ``@dataclass`` decorator. Just imagine there are many other features in your class..." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``store_attr``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.597273Z", | |
"start_time": "2021-02-08T19:59:38.582313Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a, b, c): \n store_attr()\n \nfoo = Foo(5, 4, 3)\nfoo.a, foo.b, foo.c", | |
"execution_count": 19, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(5, 4, 3)" | |
}, | |
"execution_count": 19, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "``store_attr`` is more flexible than using ``@dataclass``. For example, you can exclude some attributes with the option ``but``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.613232Z", | |
"start_time": "2021-02-08T19:59:38.599268Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a, b, c): \n store_attr(but=['c'])\n \nfoo = Foo(5, 4, 3)\nfoo.a, foo.b, hasattr(foo, 'c')", | |
"execution_count": 20, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(5, 4, False)" | |
}, | |
"execution_count": 20, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Basic representation function: ``basic_repr``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.631183Z", | |
"start_time": "2021-02-08T19:59:38.615229Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a, b=2, c=3):\n store_attr() # `store_attr` was discussed previously\n \nfoo = Foo(1)\nfoo", | |
"execution_count": 21, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "<__main__.Foo at 0x1f11e7007f0>" | |
}, | |
"execution_count": 21, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ The default ``__repr__`` method is not very telling!" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``basic_repr``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.650133Z", | |
"start_time": "2021-02-08T19:59:38.633179Z" | |
}, | |
"scrolled": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a, b=2, c=3):\n store_attr() # `store_attr` was discussed previously\n \n __repr__ = basic_repr('a, b, c')\n \nfoo = Foo(1)\nfoo", | |
"execution_count": 22, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "Foo(a=1, b=2, c=3)" | |
}, | |
"execution_count": 22, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Optional pre/post-init: ``PrePostInitMeta``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.671076Z", | |
"start_time": "2021-02-08T19:59:38.657150Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self):\n self.a = 'hello'\n \nclass FooBar(Foo):\n def __init__(self):\n super().__init__()\n self.b = 42\n\nclass FooBaz(Foo):\n def __init__(self):\n super().__init__()\n self.c = 51\n\nfoo_bar = FooBar()\nfoo_bar.a", | |
"execution_count": 23, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'hello'" | |
}, | |
"execution_count": 23, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``PrePostInitMeta``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.691575Z", | |
"start_time": "2021-02-08T19:59:38.674069Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self):\n self.a = 'hello'\n \nclass Foo2(Foo, metaclass=PrePostInitMeta):\n def __pre_init__(self, *args, **kwargs):\n super().__init__() \n \nclass FooBar(Foo2):\n def __init__(self):\n self.b = 42\n\nclass FooBaz(Foo2):\n def __init__(self):\n self.c = 51\n\nfoo_bar = FooBar()\nfoo_bar.a", | |
"execution_count": 24, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'hello'" | |
}, | |
"execution_count": 24, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "More generally, ``PrePostInitMeta`` allows you to define a ``__pre_init__`` and/or a ``__post_init__`` that will be used in the subclasses." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Create a class on-the-fly: ``mk_class``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.704366Z", | |
"start_time": "2021-02-08T19:59:38.693569Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def first_method(self):\n return self.a\n\ndef second_method(self):\n return self.b\n\nmk_class('Foo', 'a', 'b',\n sup=dict,\n doc='Nice documentation',\n funcs=(first_method, second_method))", | |
"execution_count": 25, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.719501Z", | |
"start_time": "2021-02-08T19:59:38.706535Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo = Foo(a=1, b=2, c=3)", | |
"execution_count": 26, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.739481Z", | |
"start_time": "2021-02-08T19:59:38.721906Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo.a, foo.b, foo.c", | |
"execution_count": 27, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(1, 2, 3)" | |
}, | |
"execution_count": 27, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.755508Z", | |
"start_time": "2021-02-08T19:59:38.741478Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo.first_method(), foo.second_method()", | |
"execution_count": 28, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(1, 2)" | |
}, | |
"execution_count": 28, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.770364Z", | |
"start_time": "2021-02-08T19:59:38.757401Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo.__doc__", | |
"execution_count": 29, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Nice documentation'" | |
}, | |
"execution_count": 29, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Wrap a function into a class: ``wrap_class``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.780962Z", | |
"start_time": "2021-02-08T19:59:38.772359Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "@wrap_class('Foo', a=1)\ndef bar(self, x):\n return self.a + x", | |
"execution_count": 30, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.797915Z", | |
"start_time": "2021-02-08T19:59:38.782956Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo = Foo()\nfoo.a", | |
"execution_count": 31, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "1" | |
}, | |
"execution_count": 31, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.810882Z", | |
"start_time": "2021-02-08T19:59:38.799910Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "foo.bar(2)", | |
"execution_count": 32, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "3" | |
}, | |
"execution_count": 32, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Classes and Functions" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Monkey patching: ``patch``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.825842Z", | |
"start_time": "2021-02-08T19:59:38.812875Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Foo:\n def __init__(self, a):\n self.a = a\n\n@patch\ndef f(self: Foo, b):\n return self.a + b\n\nfoo = Foo(a=1)\nfoo.f(b=2)", | |
"execution_count": 33, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "3" | |
}, | |
"execution_count": 33, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ Not very useful for your own classes, because you can write ``f`` directly in the code of the class. But can be very handy for classes from external packages!" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.836812Z", | |
"start_time": "2021-02-08T19:59:38.827835Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "from pathlib import Path", | |
"execution_count": 34, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.851812Z", | |
"start_time": "2021-02-08T19:59:38.839048Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "@patch\ndef f(self: Path):\n return self.absolute().parts", | |
"execution_count": 35, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.865733Z", | |
"start_time": "2021-02-08T19:59:38.854763Z" | |
}, | |
"scrolled": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "p = Path('.')\np.f()", | |
"execution_count": 36, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "('d:\\\\',\n 'Dropbox',\n 'A',\n '01_Recherche',\n 'Groupe de lecture',\n '2021 02 10 Fastcore (Fanfan)')" | |
}, | |
"execution_count": 36, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Improved ``pathlib.Path``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.881832Z", | |
"start_time": "2021-02-08T19:59:38.867728Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "from fastcore.utils import *\nfrom pathlib import Path\np = Path('.')\np.ls() # you don't get this with vanilla Pathlib.Path!!", | |
"execution_count": 37, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#2) [Path('.ipynb_checkpoints'),Path('Fastcore presentation.ipynb')]" | |
}, | |
"execution_count": 37, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ Fastcore uses ``patch`` to add new functionalities to ``Path``." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Other examples:" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "* ``Path.readlines``: same as ``with open('somefile', 'r') as f: f.readlines()``,\n* ``Path.read``: same as ``with open('somefile', 'r') as f: f.read()``,\n* ``Path.save``: saves file as pickle,\n* ``Path.load``: loads pickle file,\n* Etc." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### More concise lambdas: ``Self``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.892764Z", | |
"start_time": "2021-02-08T19:59:38.884108Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f = lambda a: a.sum()\nf(np.array([1, 2, 3, 4, 5]))", | |
"execution_count": 38, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "15" | |
}, | |
"execution_count": 38, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Use ``Self`` (with capital S):" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.908778Z", | |
"start_time": "2021-02-08T19:59:38.894726Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f = Self.sum()\nf(np.array([1, 2, 3, 4, 5]))", | |
"execution_count": 39, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "15" | |
}, | |
"execution_count": 39, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "More interesting example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.929633Z", | |
"start_time": "2021-02-08T19:59:38.910682Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "df = pd.DataFrame({'Some Column': ['a', 'a', 'b', 'b', ], \n 'Another Column': [5, 7, 50, 70]})\ndf", | |
"execution_count": 40, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>Some Column</th>\n <th>Another Column</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>a</td>\n <td>5</td>\n </tr>\n <tr>\n <th>1</th>\n <td>a</td>\n <td>7</td>\n </tr>\n <tr>\n <th>2</th>\n <td>b</td>\n <td>50</td>\n </tr>\n <tr>\n <th>3</th>\n <td>b</td>\n <td>70</td>\n </tr>\n </tbody>\n</table>\n</div>", | |
"text/plain": " Some Column Another Column\n0 a 5\n1 a 7\n2 b 50\n3 b 70" | |
}, | |
"execution_count": 40, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.949656Z", | |
"start_time": "2021-02-08T19:59:38.932330Z" | |
}, | |
"scrolled": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f = Self.groupby('Some Column').mean()\nf(df)", | |
"execution_count": 41, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>Another Column</th>\n </tr>\n <tr>\n <th>Some Column</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>a</th>\n <td>6</td>\n </tr>\n <tr>\n <th>b</th>\n <td>60</td>\n </tr>\n </tbody>\n</table>\n</div>", | |
"text/plain": " Another Column\nSome Column \na 6\nb 60" | |
}, | |
"execution_count": 41, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Function applying on a particular attribute: ``using_attr``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.960551Z", | |
"start_time": "2021-02-08T19:59:38.951574Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Author:\n def __init__(self, name):\n self.name = name", | |
"execution_count": 42, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.974514Z", | |
"start_time": "2021-02-08T19:59:38.963541Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def f(x):\n return str.upper(x.name)", | |
"execution_count": 43, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:38.987021Z", | |
"start_time": "2021-02-08T19:59:38.977504Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "author = Author('Jeremy')\nf(author)", | |
"execution_count": 44, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'JEREMY'" | |
}, | |
"execution_count": 44, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.002940Z", | |
"start_time": "2021-02-08T19:59:38.989975Z" | |
}, | |
"slideshow": { | |
"slide_type": "subslide" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Author:\n def __init__(self, name):\n self.name = name", | |
"execution_count": 45, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.014907Z", | |
"start_time": "2021-02-08T19:59:39.004934Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "f = using_attr(str.upper, 'name')", | |
"execution_count": 46, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.030941Z", | |
"start_time": "2021-02-08T19:59:39.016903Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "author = Author('Jeremy')\nf(author)", | |
"execution_count": 47, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'JEREMY'" | |
}, | |
"execution_count": 47, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Delegation" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Delegated inheritance: ``delegates``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.042834Z", | |
"start_time": "2021-02-08T19:59:39.032861Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class WebPage:\n def __init__(self, title, category=\"General\", author=\"Jeremy\"):\n self.title = title\n self.category = category\n self.author = author\n \nclass ProductPage(WebPage):\n def __init__(self, title, price, cost, category=\"General\", author=\"Jeremy\"):\n super().__init__(title=title, category=category, author=author)\n self.price = price\n self.cost = cost", | |
"execution_count": 48, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ We violate the \"Don't Repeat Yourself\" principle: we duplicate the list of parameters names and their defaults.\n\n$\\Rightarrow$ Later, if we change the default author in ``WebPage`` to Rachel, we may forget to do it in ``ProductPage``." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Using classic ``**kwargs`` to solve the problem:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.056795Z", | |
"start_time": "2021-02-08T19:59:39.044830Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class WebPage:\n def __init__(self, title, category=\"General\", author=\"Jeremy\"):\n self.title = title\n self.category = category\n self.author = author\n \nclass ProductPage(WebPage):\n def __init__(self, title, price, cost, **kwargs):\n super().__init__(title=title, **kwargs)\n self.price = price\n self.cost = cost", | |
"execution_count": 49, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.083991Z", | |
"start_time": "2021-02-08T19:59:39.072754Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "help(ProductPage.__init__)", | |
"execution_count": 51, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "Help on function __init__ in module __main__:\n\n__init__(self, title, price, cost, **kwargs)\n Initialize self. See help(type(self)) for accurate signature.\n\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ Problem: the documentation is not very useful." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Solve the problem with *delegated inheritance*, using ``delegates``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.094963Z", | |
"start_time": "2021-02-08T19:59:39.086983Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class WebPage:\n def __init__(self, title, category=\"General\", author=\"Jeremy\"):\n self.title = title\n self.category = category\n self.author = author\n \n@delegates() # This line was added\nclass ProductPage(WebPage):\n def __init__(self, title, price, cost, **kwargs):\n super().__init__(title=title, **kwargs)\n self.price = price\n self.cost = cost", | |
"execution_count": 52, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.123917Z", | |
"start_time": "2021-02-08T19:59:39.112913Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "help(ProductPage.__init__)", | |
"execution_count": 54, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "Help on function __init__ in module __main__:\n\n__init__(self, title, price, cost, category='General', author='Jeremy')\n Initialize self. See help(type(self)) for accurate signature.\n\n" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "-" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Remark: ``delegates`` works also with functions, and has several optional parameters to control what you delegate." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Delegated composition: ``GetAttr``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Motivating example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.138971Z", | |
"start_time": "2021-02-08T19:59:39.126934Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Author:\n def __init__(self, name):\n self.name = name\n\nclass ProductPage: \n def __init__(self, author, price, cost):\n self.author = author\n self.price = price\n self.cost = cost\n self.name = self.author.name\n\np = ProductPage(Author(\"Jeremy\"), 1.50, 0.50)\np.name", | |
"execution_count": 55, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Jeremy'" | |
}, | |
"execution_count": 55, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "A nicer solution, in classic Python:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.152920Z", | |
"start_time": "2021-02-08T19:59:39.140839Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Author:\n def __init__(self, name):\n self.name = name\n\nclass ProductPage:\n def __init__(self, author, price, cost):\n self.author = author\n self.price = price\n self.cost = cost\n def __getattr__(self, k):\n return getattr(self.author, k)\n \np = ProductPage(Author(\"Jeremy\"), 1.50, 0.50)\np.name", | |
"execution_count": 56, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Jeremy'" | |
}, | |
"execution_count": 56, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Problem 1: we often want to choose which attributes are forwarded to the composed object.\n\nProblem 2: we lost the auto-completion. Indeed, in auto-completion you will see only:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.166772Z", | |
"start_time": "2021-02-08T19:59:39.154801Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "[o for o in dir(p) if not o.startswith('_')]", | |
"execution_count": 57, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "['author', 'cost', 'price']" | |
}, | |
"execution_count": 57, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Solve the problem with *delegated composition*, using ``GetAttr``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T20:03:45.285116Z", | |
"start_time": "2021-02-08T20:03:45.275183Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Author:\n def __init__(self, name):\n self.name = name\n\nclass ProductPage(GetAttr):\n _default = 'author'\n def __init__(self,author,price,cost):\n store_attr()\n\np = ProductPage(Author(\"Jeremy\"), 1.50, 0.50)\np.name", | |
"execution_count": 70, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "'Jeremy'" | |
}, | |
"execution_count": 70, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T12:53:19.178847Z", | |
"start_time": "2021-02-08T12:53:19.172864Z" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "What you will see in auto-completion:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.194756Z", | |
"start_time": "2021-02-08T19:59:39.183725Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "[o for o in dir(p) if not o.startswith('_')]", | |
"execution_count": 59, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "['author', 'cost', 'name', 'price']" | |
}, | |
"execution_count": 59, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "## Misc" | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Class ``L`` for lists" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Similar to standard lists, but with extra functionalities inspired by Numpy." | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.209657Z", | |
"start_time": "2021-02-08T19:59:39.196689Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "L(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)", | |
"execution_count": 60, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#11) [0,1,2,3,4,5,6,7,8,9...]" | |
}, | |
"execution_count": 60, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.222654Z", | |
"start_time": "2021-02-08T19:59:39.212650Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "L.range(11)", | |
"execution_count": 61, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#11) [0,1,2,3,4,5,6,7,8,9...]" | |
}, | |
"execution_count": 61, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ Notice the representation, different from regular lists." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "Some examples of features:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.238613Z", | |
"start_time": "2021-02-08T19:59:39.225644Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "lst = L.range(20).shuffle()\nlst", | |
"execution_count": 62, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#20) [8,15,9,12,17,1,2,3,16,6...]" | |
}, | |
"execution_count": 62, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.252540Z", | |
"start_time": "2021-02-08T19:59:39.241707Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "lst[2, 4, 6]", | |
"execution_count": 63, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#3) [9,17,2]" | |
}, | |
"execution_count": 63, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.263546Z", | |
"start_time": "2021-02-08T19:59:39.255532Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "1 + L(2, 3, 4)", | |
"execution_count": 64, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#4) [1,2,3,4]" | |
}, | |
"execution_count": 64, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.277509Z", | |
"start_time": "2021-02-08T19:59:39.265507Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "lst.argwhere(ge(15))", | |
"execution_count": 65, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(#5) [1,4,8,14,15]" | |
}, | |
"execution_count": 65, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Group items: ``groupby``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.289642Z", | |
"start_time": "2021-02-08T19:59:39.279468Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "groupby(['aa', 'ba', 'bb', 'ab'], itemgetter(0))", | |
"execution_count": 66, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "{'a': ['aa', 'ab'], 'b': ['ba', 'bb']}" | |
}, | |
"execution_count": 66, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "The package ``itertools`` also has a ``groupby``:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:39.312582Z", | |
"start_time": "2021-02-08T19:59:39.292636Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "for group, iterator in itertools.groupby(['aa', 'ba', 'bb', 'ab'], itemgetter(0)):\n print(f'Group: {group}')\n for x in iterator:\n print(f' Item: {x}')", | |
"execution_count": 67, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "Group: a\n Item: aa\nGroup: b\n Item: ba\n Item: bb\nGroup: a\n Item: ab\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "* Fastcore's ``groupby`` does not need the items to be sorted.\n* But itertools' ``groupby`` is lazy, which can be an advantage." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Simple parallel processing: ``parallel``" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T20:05:54.206528Z", | |
"start_time": "2021-02-08T20:05:51.388873Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def add_one(x, a=1): \n time.sleep(.1)\n return x + a\n\nparallel(add_one, range(50), threadpool=True, n_workers=2, progress=True)", | |
"execution_count": 71, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": "", | |
"text/plain": "<IPython.core.display.HTML object>" | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": "(#50) [1,2,3,4,5,6,7,8,9,10...]" | |
}, | |
"execution_count": 71, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Remark: to have the progress bar, you also need to install ``fastprogress``." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Notebook Functions: ``in_notebook``, ``in_colab``, ``in_ipython``" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Example:" | |
}, | |
{ | |
"metadata": { | |
"ExecuteTime": { | |
"end_time": "2021-02-08T19:59:42.097730Z", | |
"start_time": "2021-02-08T19:59:42.087333Z" | |
}, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "in_notebook(), in_colab(), in_ipython()", | |
"execution_count": 69, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "(True, False, True)" | |
}, | |
"execution_count": 69, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "$\\Rightarrow$ Allows you to know whether or not code is executing in a Jupyter Notebook, Colab, or an Ipython Shell\n\n$\\Rightarrow$ Useful if you are displaying certain types of visualizations, progress bars or animations in your code that you may want to modify or toggle depending on the environment." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "### Other features" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "* ``test``: Simple testing functions (especially for use in notebooks, from what I understood).\n* ``foundation``: Mixins, delegation, composition, and more.\n* ``xtras``: Utility functions to help with functional-style programming, parallel processing, and more.\n* ``dispatch``: Multiple dispatch methods.\n* ``transform``: Pipelines of composed partially reversible transformations." | |
}, | |
{ | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"cell_type": "markdown", | |
"source": "<font size=7><b>Thank you!</b></font>" | |
} | |
], | |
"metadata": { | |
"celltoolbar": "Diaporama", | |
"gist": { | |
"id": "", | |
"data": { | |
"description": "Fastcore_presentation.ipynb", | |
"public": true | |
} | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3", | |
"language": "python" | |
}, | |
"language_info": { | |
"name": "python", | |
"version": "3.7.3", | |
"mimetype": "text/x-python", | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"pygments_lexer": "ipython3", | |
"nbconvert_exporter": "python", | |
"file_extension": ".py" | |
}, | |
"toc": { | |
"nav_menu": {}, | |
"number_sections": true, | |
"sideBar": true, | |
"skip_h1_title": true, | |
"base_numbering": 1, | |
"title_cell": "Table of Contents", | |
"title_sidebar": "Contents", | |
"toc_cell": true, | |
"toc_position": {}, | |
"toc_section_display": true, | |
"toc_window_display": false | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment