diff --git a/.makim.yaml b/.makim.yaml index a362a493..67d5362a 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -14,7 +14,7 @@ groups: # and convert them to Markdown named 'index.md' find "$SEARCH_DIR" -path "*/.ipynb_checkpoints/*" -prune -o -name \ "*.ipynb" -exec sh -c \ - 'jupyter nbconvert --to markdown --output-dir "$(dirname "$0")" --output "index" "$0"' {} \; + 'jupyter nbconvert --to markdown --template=theme/custom-markdown.tpl --output-dir "$(dirname "$0")" --output "index" "$0"' {} \; build: help: build the static page diff --git a/conda/dev.yaml b/conda/dev.yaml index d4ed5e13..92372471 100644 --- a/conda/dev.yaml +++ b/conda/dev.yaml @@ -13,3 +13,4 @@ dependencies: - mkdocs-rss-plugin <1.9.0 - jupyterlab - nbconvert + - pymdown-extensions diff --git a/mkdocs.yml b/mkdocs.yml index 31b1648b..03ce28b6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,10 @@ site_dir: build theme: name: null custom_dir: 'theme/' + features: + - content.code.copy + palette: + primary: indigo extra_css: - css/style.css @@ -60,6 +64,13 @@ nav: markdown_extensions: - toc: permalink: " #" + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences plugins: - blogging: diff --git a/pages/blog/ibis-framework/index.md b/pages/blog/ibis-framework/index.md index 711ea6ad..dff14cfa 100644 --- a/pages/blog/ibis-framework/index.md +++ b/pages/blog/ibis-framework/index.md @@ -63,187 +63,199 @@ df.head() +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
citycity_ibge_codedateepidemiological_weekestimated_populationestimated_population_2019is_lastis_repeatedlast_available_confirmedlast_available_confirmed_per_100k_inhabitantslast_available_datelast_available_death_ratelast_available_deathsorder_for_placeplace_typestatenew_confirmednew_deaths
0Rio Branco1200401.02020-03-17202012413418.0407319.0FalseFalse30.725662020-03-170.001cityAC30
1NaN12.02020-03-17202012894470.0881935.0FalseFalse30.335392020-03-170.001stateAC30
2Rio Branco1200401.02020-03-18202012413418.0407319.0FalseFalse30.725662020-03-180.002cityAC00
3NaN12.02020-03-18202012894470.0881935.0FalseFalse30.335392020-03-180.002stateAC00
4Rio Branco1200401.02020-03-19202012413418.0407319.0FalseFalse40.967542020-03-190.003cityAC10
citycity_ibge_codedateepidemiological_weekestimated_populationestimated_population_2019is_lastis_repeatedlast_available_confirmedlast_available_confirmed_per_100k_inhabitantslast_available_datelast_available_death_ratelast_available_deathsorder_for_placeplace_typestatenew_confirmednew_deaths
0Rio Branco1200401.02020-03-17202012413418.0407319.0FalseFalse30.725662020-03-170.001cityAC30
1NaN12.02020-03-17202012894470.0881935.0FalseFalse30.335392020-03-170.001stateAC30
2Rio Branco1200401.02020-03-18202012413418.0407319.0FalseFalse30.725662020-03-180.002cityAC00
3NaN12.02020-03-18202012894470.0881935.0FalseFalse30.335392020-03-180.002stateAC00
4Rio Branco1200401.02020-03-19202012413418.0407319.0FalseFalse40.967542020-03-190.003cityAC10
+ ```python df.info() ``` - - RangeIndex: 3853648 entries, 0 to 3853647 - Data columns (total 18 columns): - # Column Dtype - --- ------ ----- - 0 city object - 1 city_ibge_code float64 - 2 date object - 3 epidemiological_week int64 - 4 estimated_population float64 - 5 estimated_population_2019 float64 - 6 is_last bool - 7 is_repeated bool - 8 last_available_confirmed int64 - 9 last_available_confirmed_per_100k_inhabitants float64 - 10 last_available_date object - 11 last_available_death_rate float64 - 12 last_available_deaths int64 - 13 order_for_place int64 - 14 place_type object - 15 state object - 16 new_confirmed int64 - 17 new_deaths int64 - dtypes: bool(2), float64(5), int64(6), object(5) - memory usage: 477.8+ MB - +
+

+ OUTPUT + +

+
+  
+
+RangeIndex: 3853648 entries, 0 to 3853647
+Data columns (total 18 columns):
+#   Column                                         Dtype
+---  ------                                         -----
+0   city                                           object
+1   city_ibge_code                                 float64
+2   date                                           object
+3   epidemiological_week                           int64
+4   estimated_population                           float64
+5   estimated_population_2019                      float64
+6   is_last                                        bool
+7   is_repeated                                    bool
+8   last_available_confirmed                       int64
+9   last_available_confirmed_per_100k_inhabitants  float64
+10  last_available_date                            object
+11  last_available_death_rate                      float64
+12  last_available_deaths                          int64
+13  order_for_place                                int64
+14  place_type                                     object
+15  state                                          object
+16  new_confirmed                                  int64
+17  new_deaths                                     int64
+dtypes: bool(2), float64(5), int64(6), object(5)
+memory usage: 477.8+ MB
+
+
+
+
E para demonstrar o verdadeiro poder do Ibis, iremos transformar nosso arquivo CSV em uma Base de Dados SQL. Na instalação padrão do Ibis, o backend SQL é o `sqlite3`, então nos exemplos a seguir utilizaremos SQLite para realizar buscas na base de dados. Caso queira utilizar outra Engine SQL, como [BigQuery](https://github.com/ibis-project/ibis-bigquery/) ou [Postgres](https://ibis-project.org/docs/3.1.0/backends/PostgreSQL/), acesse a [documentação oficial](https://ibis-project.org/docs/3.1.0/backends/PostgreSQL/) e siga instruções de instalação. @@ -255,7 +267,17 @@ df.to_sql('casos_covid19_BR', sqlite3.connect('data/casof.db')) - 3853648 +
+

+ OUTPUT + +

+
+  
+3853648
+
+
+
@@ -294,9 +316,19 @@ print(data_min.execute()) # Dia da primeira entrada registrada na base de dados print(data_max.execute()) # Dia da última entrada registrada na base de dados ``` - 2020-02-25 - 2022-03-27 - +
+

+ OUTPUT + +

+
+  
+2020-02-25
+2022-03-27
+
+
+
+
## E por que usar Ibis ao invés das ferramentas SQL diretamente? @@ -312,9 +344,19 @@ Por exemplo: já vimos que é possível criar buscas SQL através de expressões print(data_min.compile()) ``` - SELECT min(t0.date) AS first_entry - FROM main."casos_covid19_BR" AS t0 - +
+

+ OUTPUT + +

+
+  
+SELECT min(t0.date) AS first_entry
+FROM main."casos_covid19_BR" AS t0
+
+
+
+
Um dos pontos chave do Ibis, é a possibilidade de criar Expressões com o resultado de interesse, renomeá-las, e utilizá-las para outras buscas sem precisar repetir código: @@ -348,9 +390,19 @@ epiweek_covid = casos.group_by('epidemiological_week').aggregate(( print(epiweek_covid.compile()) ``` - SELECT t0.epidemiological_week, sum(t0.new_confirmed) AS total_new_cases, sum(t0.new_deaths) AS total_new_deaths - FROM main."casos_covid19_BR" AS t0 GROUP BY t0.epidemiological_week - +
+

+ OUTPUT + +

+
+  
+SELECT t0.epidemiological_week, sum(t0.new_confirmed) AS total_new_cases, sum(t0.new_deaths) AS total_new_deaths
+FROM main."casos_covid19_BR" AS t0 GROUP BY t0.epidemiological_week
+
+
+
+
Lembra que o Ibis utiliza o Pandas como Backend de execução? Podemos agora salvar o Pandas DataFrame gerado na execução em uma variável para termos acesso às funções do Pandas: @@ -363,67 +415,69 @@ df.head() +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
epidemiological_weektotal_new_casestotal_new_deaths
020200940
1202010330
22020112310
3202012193530
42020135476183
epidemiological_weektotal_new_casestotal_new_deaths
020200940
1202010330
22020112310
3202012193530
42020135476183
+ ```python df['week'] = df['epidemiological_week'].astype(str).str[4:6] df['year'] = df['epidemiological_week'].astype(str).str[0:4] @@ -437,73 +491,75 @@ df.head() +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
yearweektotal_new_casestotal_new_deaths
020200940
1202010330
22020112310
3202012193530
42020135476183
yearweektotal_new_casestotal_new_deaths
020200940
1202010330
22020112310
3202012193530
42020135476183
+ ```python df = df.head(15) plt.bar(df.week, df.total_new_cases) @@ -565,9 +621,19 @@ print(percentage_cases.compile().compile( compile_kwargs={"literal_binds": True})) ``` - SELECT (t0.new_confirmed / t0.estimated_population) * 100 AS porc_cases - FROM main."casos_covid19_BR" AS t0 - +
+

+ OUTPUT + +

+
+  
+SELECT (t0.new_confirmed / t0.estimated_population) * 100 AS porc_cases
+FROM main."casos_covid19_BR" AS t0
+
+
+
+
```python @@ -576,9 +642,19 @@ print(south_br.compile().compile( compile_kwargs={"literal_binds": True})) ``` - SELECT CASE WHEN (t0.state = 'SC') THEN 'Santa Catarina' WHEN (t0.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t0.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END AS "Regiao Sul" - FROM main."casos_covid19_BR" AS t0 - +
+

+ OUTPUT + +

+
+  
+SELECT CASE WHEN (t0.state = 'SC') THEN 'Santa Catarina' WHEN (t0.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t0.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END AS "Regiao Sul"
+FROM main."casos_covid19_BR" AS t0
+
+
+
+
Agora que temos a porcentagem de casos e a região separadas em duas variáveis, podemos agregar as buscas e encontrar as porcentagem de casos nos estados em questão e retorná-lo em Dataframe: @@ -589,11 +665,21 @@ sul = casos.group_by(south_br).aggregate(percentage_cases.mean().name('Media Cas print(sul.compile().compile(compile_kwargs={"literal_binds": True})) ``` - SELECT t0."Regiao Sul", t0."Media Casos" - FROM (SELECT CASE WHEN (t1.state = 'SC') THEN 'Santa Catarina' WHEN (t1.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t1.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END AS "Regiao Sul", avg((t1.new_confirmed / t1.estimated_population) * 100) AS "Media Casos" - FROM main."casos_covid19_BR" AS t1 GROUP BY CASE WHEN (t1.state = 'SC') THEN 'Santa Catarina' WHEN (t1.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t1.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END) AS t0 - WHERE t0."Regiao Sul" IS NOT NULL AND t0."Media Casos" IS NOT NULL - +
+

+ OUTPUT + +

+
+  
+SELECT t0."Regiao Sul", t0."Media Casos"
+FROM (SELECT CASE WHEN (t1.state = 'SC') THEN 'Santa Catarina' WHEN (t1.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t1.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END AS "Regiao Sul", avg((t1.new_confirmed / t1.estimated_population) * 100) AS "Media Casos"
+FROM main."casos_covid19_BR" AS t1 GROUP BY CASE WHEN (t1.state = 'SC') THEN 'Santa Catarina' WHEN (t1.state = 'RS') THEN 'Rio Grande do Sul' WHEN (t1.state = 'PR') THEN 'Parana' ELSE CAST(NULL AS TEXT) END) AS t0
+WHERE t0."Regiao Sul" IS NOT NULL AND t0."Media Casos" IS NOT NULL
+
+
+
+
```python @@ -604,50 +690,52 @@ sul.execute() +
- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
Regiao SulMedia Casos
0Parana0.018670
1Rio Grande do Sul0.028637
2Santa Catarina0.029332
Regiao SulMedia Casos
0Parana0.018670
1Rio Grande do Sul0.028637
2Santa Catarina0.029332
+ ## Conclusão Como pudemos ver, o Ibis é uma ferramenta poderosa para acelerar sua análise de dados, capaz de integrar diferentes engines SQL com o Pandas, o Framework traz melhorias de performance e legibilidade ao código Python. Crie e realize buscas SQL como se estivesse trabalhando com um Pandas DataFrame, mas com uma economia de memória e alta portabilidade! Com o Ibis é possível utilizar o mesmo padrão de trabalho para desenvolvimento e produção, acelerar seus testes unitários, escalonar a aplicação para diferentes bases de dados, e muito mais! diff --git a/pages/blog/makim-efficient-workflows-with-makims-working-directory/index.md b/pages/blog/makim-efficient-workflows-with-makims-working-directory/index.md index 94a96373..b3256e33 100644 --- a/pages/blog/makim-efficient-workflows-with-makims-working-directory/index.md +++ b/pages/blog/makim-efficient-workflows-with-makims-working-directory/index.md @@ -165,8 +165,18 @@ groups: # Add commands for building Vue.js frontend ``` - Overwriting .makim.yaml +
+

+ OUTPUT + +

+
+  
+Overwriting .makim.yaml
 
+
+
+
```python @@ -176,11 +186,21 @@ groups: !makim --makim-file ./.makim.yaml frontend_vue.build ``` - Linting Python code... - Running Java backend tests... - Running React frontend tests... - Building Vue.js frontend... - +
+

+ OUTPUT + +

+
+  
+Linting Python code...
+Running Java backend tests...
+Running React frontend tests...
+Building Vue.js frontend...
+
+
+
+
## Conclusion diff --git a/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/header.png b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/header.png new file mode 100644 index 00000000..bbcca2fd Binary files /dev/null and b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/header.png differ diff --git a/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.ipynb b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.ipynb new file mode 100644 index 00000000..1fd01fb7 --- /dev/null +++ b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "f170f83a-7776-4ed8-b295-4eb57b5d0219", + "metadata": {}, + "source": [ + "---\n", + "title: \"Unlocking the Power of Multiple Dispatch in Python with Plum-Dispatch\"\n", + "slug: unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch\n", + "date: 2024-01-05\n", + "authors: [\"Ivan Ogasawara\"]\n", + "tags: [open-source, multiple-dispatch, python]\n", + "categories: [python]\n", + "description: |\n", + " ...\n", + "thumbnail: \"/header.png\"\n", + "template: \"blog-post.html\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "46aadcef-e7c2-480a-bf3a-bbe20d09cc3b", + "metadata": {}, + "source": [ + "Python, known for its simplicity and readability, sometimes requires a bit of creativity when it comes to implementing certain programming paradigms. One such paradigm is multiple dispatch (or multimethods), which allows functions to behave differently based on the type of their arguments. While not natively supported in Python, this feature can be incredibly powerful, particularly in complex applications such as mathematical computations, data processing, or when working with abstract syntax trees (ASTs). This is where `plum-dispatch` comes into play.\n", + "\n", + "## What is Multiple Dispatch?\n", + "\n", + "Multiple dispatch is a feature where the function to be executed is determined by the types of multiple arguments. This is different from single dispatch (which Python supports natively via the `functools.singledispatch` decorator), where the function called depends only on the type of the first argument.\n", + "\n", + "## Introducing Plum-Dispatch\n", + "\n", + "`plum-dispatch` is a Python library that provides an efficient and easy-to-use implementation of multiple dispatch. It allows you to define multiple versions of a function, each tailored to different types of input arguments.\n", + "\n", + "### Installation\n", + "\n", + "First things first, let's install `plum-dispatch`:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5d233782-8974-4758-aac8-0a6cfe757376", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install plum-dispatch -q" + ] + }, + { + "cell_type": "markdown", + "id": "84a8444d-6f2b-442e-8ab8-44f92c645c01", + "metadata": {}, + "source": [ + "### Basic Usage\n", + "\n", + "To demonstrate the basic usage of `plum-dispatch`, let's start with a simple example. Suppose we have a function that needs to behave differently when passed an integer versus when it's passed a string." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8a1320a3-4531-4d60-80af-4c8ae6301ad4", + "metadata": {}, + "outputs": [], + "source": [ + "from plum import dispatch\n", + "\n", + "class Processor:\n", + " @dispatch\n", + " def process(self, data: int):\n", + " return f\"Processing integer: {data}\"\n", + "\n", + " @dispatch\n", + " def process(self, data: str):\n", + " return f\"Processing string: {data}\"" + ] + }, + { + "cell_type": "markdown", + "id": "b396547c-3601-4d32-b6b3-25eba3c13be0", + "metadata": {}, + "source": [ + "In this example, `Processor` has two `process` methods, one for integers and one for strings. `plum-dispatch` takes care of determining which method to call based on the type of `data`." + ] + }, + { + "cell_type": "markdown", + "id": "6f2f654d-c142-408b-981c-ea0f4875a36d", + "metadata": {}, + "source": [ + "### Advanced Example: Working with ASTs\n", + "\n", + "`plum-dispatch` shines in more complex scenarios, such as when working with different types of nodes in an abstract syntax tree. Let's create a simple AST representation with different node types and a visitor class to process these nodes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4314c83a-8c69-41e7-be27-7738cd1003a9", + "metadata": {}, + "outputs": [], + "source": [ + "class StringNode:\n", + " def __init__(self, value):\n", + " self.value = value\n", + "\n", + "class NumberNode:\n", + " def __init__(self, value):\n", + " self.value = value\n", + "\n", + "class BaseASTVisitor:\n", + " @dispatch\n", + " def visit(self, node: StringNode):\n", + " raise Exception(\"Not implemented yet.\")\n", + "\n", + " @dispatch\n", + " def visit(self, node: NumberNode):\n", + " raise Exception(\"Not implemented yet.\")\n", + "\n", + "class ASTVisitor(BaseASTVisitor):\n", + " @dispatch\n", + " def visit(self, node: StringNode):\n", + " return f\"Visited StringNode with value: {node.value}\"\n", + "\n", + " @dispatch\n", + " def visit(self, node: NumberNode):\n", + " return f\"Visited NumberNode with value: {node.value}\"" + ] + }, + { + "cell_type": "markdown", + "id": "d04e9401-e2a3-415a-9b85-0d1e0bf459e6", + "metadata": {}, + "source": [ + "With `plum-dispatch`, our `ASTVisitor` can have a single `visit` method that behaves differently depending on whether it's visiting a `StringNode` or a `NumberNode`." + ] + }, + { + "cell_type": "markdown", + "id": "312c3a2f-27b4-4abc-ab0d-ab6cd531066d", + "metadata": {}, + "source": [ + "### Putting It All Together\n", + "Now, let's see `plum-dispatch` in action:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a321b28b-25ab-47a7-bd28-bbed52107952", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing integer: 123\n", + "Processing string: abc\n", + "Visited StringNode with value: Hello\n", + "Visited NumberNode with value: 456\n" + ] + } + ], + "source": [ + "processor = Processor()\n", + "print(processor.process(123)) # \"Processing integer: 123\"\n", + "print(processor.process(\"abc\")) # \"Processing string: abc\"\n", + "\n", + "visitor = ASTVisitor()\n", + "print(visitor.visit(StringNode(\"Hello\"))) # \"Visited StringNode with value: Hello\"\n", + "print(visitor.visit(NumberNode(456))) # \"Visited NumberNode with value: 456\"" + ] + }, + { + "cell_type": "markdown", + "id": "1045a8fa-87ca-44fa-86ea-425429377ee1", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "`plum-dispatch` offers a neat and powerful way to implement multiple dispatch in Python, making your code more modular, readable, and elegant. Whether you're dealing with simple data types or complex structures like ASTs, `plum-dispatch` can help you write more efficient and maintainable code.\n", + "\n", + "For more complex examples and advanced usage, check out the [plum-dispatch documentation](https://github.com/wesselb/plum)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.md b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.md new file mode 100644 index 00000000..66172a73 --- /dev/null +++ b/pages/blog/unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch/index.md @@ -0,0 +1,121 @@ +--- +title: "Unlocking the Power of Multiple Dispatch in Python with Plum-Dispatch" +slug: unlocking-the-power-of-multiple-dispatch-in-python-with-plum-dispatch +date: 2024-01-05 +authors: ["Ivan Ogasawara"] +tags: [open-source, multiple-dispatch, python] +categories: [python] +description: | + ... +thumbnail: "/header.png" +template: "blog-post.html" +--- +Python, known for its simplicity and readability, sometimes requires a bit of creativity when it comes to implementing certain programming paradigms. One such paradigm is multiple dispatch (or multimethods), which allows functions to behave differently based on the type of their arguments. While not natively supported in Python, this feature can be incredibly powerful, particularly in complex applications such as mathematical computations, data processing, or when working with abstract syntax trees (ASTs). This is where `plum-dispatch` comes into play. + +## What is Multiple Dispatch? + +Multiple dispatch is a feature where the function to be executed is determined by the types of multiple arguments. This is different from single dispatch (which Python supports natively via the `functools.singledispatch` decorator), where the function called depends only on the type of the first argument. + +## Introducing Plum-Dispatch + +`plum-dispatch` is a Python library that provides an efficient and easy-to-use implementation of multiple dispatch. It allows you to define multiple versions of a function, each tailored to different types of input arguments. + +### Installation + +First things first, let's install `plum-dispatch`: + + +```python +!pip install plum-dispatch -q +``` + +### Basic Usage + +To demonstrate the basic usage of `plum-dispatch`, let's start with a simple example. Suppose we have a function that needs to behave differently when passed an integer versus when it's passed a string. + + +```python +from plum import dispatch + +class Processor: + @dispatch + def process(self, data: int): + return f"Processing integer: {data}" + + @dispatch + def process(self, data: str): + return f"Processing string: {data}" +``` + +In this example, `Processor` has two `process` methods, one for integers and one for strings. `plum-dispatch` takes care of determining which method to call based on the type of `data`. + +### Advanced Example: Working with ASTs + +`plum-dispatch` shines in more complex scenarios, such as when working with different types of nodes in an abstract syntax tree. Let's create a simple AST representation with different node types and a visitor class to process these nodes. + + +```python +class StringNode: + def __init__(self, value): + self.value = value + +class NumberNode: + def __init__(self, value): + self.value = value + +class BaseASTVisitor: + @dispatch + def visit(self, node: StringNode): + raise Exception("Not implemented yet.") + + @dispatch + def visit(self, node: NumberNode): + raise Exception("Not implemented yet.") + +class ASTVisitor(BaseASTVisitor): + @dispatch + def visit(self, node: StringNode): + return f"Visited StringNode with value: {node.value}" + + @dispatch + def visit(self, node: NumberNode): + return f"Visited NumberNode with value: {node.value}" +``` + +With `plum-dispatch`, our `ASTVisitor` can have a single `visit` method that behaves differently depending on whether it's visiting a `StringNode` or a `NumberNode`. + +### Putting It All Together +Now, let's see `plum-dispatch` in action: + + +```python +processor = Processor() +print(processor.process(123)) # "Processing integer: 123" +print(processor.process("abc")) # "Processing string: abc" + +visitor = ASTVisitor() +print(visitor.visit(StringNode("Hello"))) # "Visited StringNode with value: Hello" +print(visitor.visit(NumberNode(456))) # "Visited NumberNode with value: 456" +``` + +
+

+ OUTPUT + +

+
+  
+Processing integer: 123
+Processing string: abc
+Visited StringNode with value: Hello
+Visited NumberNode with value: 456
+
+
+
+
+ +## Conclusion + +`plum-dispatch` offers a neat and powerful way to implement multiple dispatch in Python, making your code more modular, readable, and elegant. Whether you're dealing with simple data types or complex structures like ASTs, `plum-dispatch` can help you write more efficient and maintainable code. + +For more complex examples and advanced usage, check out the [plum-dispatch documentation](https://github.com/wesselb/plum). diff --git a/theme/css/styles.css b/theme/css/styles.css index 42bb4dee..1b0a4451 100644 --- a/theme/css/styles.css +++ b/theme/css/styles.css @@ -1512,7 +1512,7 @@ code{ word-break:break-all } code.noClass{ - --inlineColor: rgb(194, 29, 0); + --inlineColor: #d63384; color:var(--inlineColor); display:inline; line-break:anywhere @@ -1563,11 +1563,12 @@ code.noClass{ margin:0 } .highlight pre{ - color:var(--light) !important; + /* color:var(--light) !important; */ border-radius:4px; font-family:'Monaco', monospace; padding-top:1.5rem; - padding-bottom:2rem + padding-bottom:2rem; + padding-left: 10px; } .highlight table{ display:grid; @@ -1755,5 +1756,49 @@ a.page-number.active { .highlight *{ - color: white!important; + background: #44002e!important; +} + +.highlight span.n, +.highlight span.o { + color: #ff7b72; +} + +.highlight span.fm { + color: #447fcf; +} + +.highlight span.p { + color: white; +} + +.highlight span.bp { + color: #ffa657; +} + +.language-text.highlight *{ + background-color: #333!important; +} + +.language-text.highlight code.noClass{ + --inlineColor: white!important; + color: var(--inlineColor)!important; +} + +.language-text.highlight .output { + padding-bottom: 0px; +} + +.code-output { + border-top: 10px solid #383838; +} + + +.code-label { + font-size: 18px; + border-bottom: 1px solid #fafafa; + line-height: 23.4px; + position: absolute; + right: 25px; + margin-top: 15px; } diff --git a/theme/custom-markdown.tpl b/theme/custom-markdown.tpl new file mode 100644 index 00000000..1b215ea9 --- /dev/null +++ b/theme/custom-markdown.tpl @@ -0,0 +1,29 @@ +{% extends 'markdown/index.md.j2'%} +{% block data_text %} +
+

+ OUTPUT + +

+
+  {{ super().split('\n') | map('trim') | join('\n') }}
+
+
+{% endblock data_text %} + +{% block data_html %} +{{ super().split('\n') | map('trim') | join('\n') }} +{% endblock data_html %} + + +{% block stream %} +
+

+ OUTPUT + +

+
+  {{ super().split('\n') | map('trim') | join('\n') }}
+
+
+{% endblock stream %}