Skip to content

Instantly share code, notes, and snippets.

@francois-durand
Created February 8, 2021 20:12
Show Gist options
  • Save francois-durand/f75845430b2d973cc91ff20549fe2c54 to your computer and use it in GitHub Desktop.
Save francois-durand/f75845430b2d973cc91ff20549fe2c54 to your computer and use it in GitHub Desktop.
Fastcore_presentation.ipynb
Display the source blob
Display the rendered blob
Raw
{
"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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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