Last active
September 23, 2022 15:24
-
-
Save Hasenpfote/903c3fd2ee7db16f31e18860458a6d4b to your computer and use it in GitHub Desktop.
How to hook properties with Python3 dataclasses.
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": [ | |
{ | |
"cell_type": "markdown", | |
"id": "845a17a1-4d8f-4e19-99cd-f525f1a9d37d", | |
"metadata": {}, | |
"source": [ | |
"# How to hook properties with Python3 dataclasses.\n", | |
"- [Reconciling Dataclasses And Properties In Python](https://florimond.dev/en/posts/2018/10/reconciling-dataclasses-and-properties-in-python/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"id": "3c893311-90f4-4d9d-9cbd-bcc94bc31d7e", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Python 3.8.3\n" | |
] | |
} | |
], | |
"source": [ | |
"!python --version" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f62a894f-b735-4b78-968f-b961b68c365e", | |
"metadata": {}, | |
"source": [ | |
"## Use mypy to check for type errors.\n", | |
"[nb-mypy](https://pypi.org/project/nb-mypy/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"id": "346d3937-5d0d-41e6-a542-32cd5a154048", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"Version 1.0.4\n" | |
] | |
} | |
], | |
"source": [ | |
"%load_ext nb_mypy" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"id": "1627d10a-f7e2-4908-aa38-1ac1a9018353", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"%nb_mypy On" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"id": "b6bb7d67-397f-453b-adfe-17a4f0ce2911", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from dataclasses import dataclass\n", | |
"from typing import Optional, Union" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "a56a8131-6e4e-4fd8-b680-5f5009440883", | |
"metadata": {}, | |
"source": [ | |
"## Want to hook properties." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"id": "1b3e1e5a-d95d-4362-8e17-640602c94d25", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"__init__: None\n", | |
"getter: Barack\n", | |
"name: Barack\n", | |
"setter: Donald\n", | |
"getter: Donald\n", | |
"name Donald\n", | |
"setter: Joe\n", | |
"getter: Joe\n", | |
"name: Joe\n", | |
"{'_name': 'Joe'}\n", | |
"------------------------------\n", | |
"__init__: George\n", | |
"getter: George\n", | |
"name: George\n", | |
"{'_name': 'George'}\n" | |
] | |
} | |
], | |
"source": [ | |
"class Test1:\n", | |
" def __init__(self, name: Optional[str] = None):\n", | |
" print('__init__:', name)\n", | |
" if name is None:\n", | |
" name = 'Barack'\n", | |
" self._name: Optional[str] = name\n", | |
"\n", | |
" @property\n", | |
" def name(self) -> Optional[str]:\n", | |
" print('getter:', self._name)\n", | |
" return self._name\n", | |
"\n", | |
" @name.setter\n", | |
" def name(self, name: str):\n", | |
" print('setter:', name)\n", | |
" self._name = name\n", | |
"\n", | |
"\n", | |
"t = Test1(name=None)\n", | |
"print('name:', t.name)\n", | |
"t.name = 'Donald'\n", | |
"print('name', t.name)\n", | |
"t.name = 'Joe'\n", | |
"print('name:', t.name)\n", | |
"print(vars(t))\n", | |
"print('-'*30)\n", | |
"t = Test1(name='George')\n", | |
"print('name:', t.name)\n", | |
"print(vars(t))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "bec14645-a582-4db9-b042-fb563fd586d1", | |
"metadata": {}, | |
"source": [ | |
"## How to do the same with dataclasses." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"id": "3360ce92-4eff-4fc9-a33a-2d89c368bc70", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"setter: None\n", | |
"__post_init__\n", | |
"getter: None\n", | |
"setter: Barack\n", | |
"getter: Barack\n", | |
"name: Barack\n", | |
"setter: Donald\n", | |
"getter: Donald\n", | |
"name: Donald\n", | |
"setter: Joe\n", | |
"getter: Joe\n", | |
"name: Joe\n", | |
"{'_name': 'Joe'}\n", | |
"------------------------------\n", | |
"setter: George\n", | |
"__post_init__\n", | |
"getter: George\n", | |
"getter: George\n", | |
"name: George\n", | |
"{'_name': 'George'}\n" | |
] | |
} | |
], | |
"source": [ | |
"@dataclass\n", | |
"class Test2:\n", | |
" name: Optional[Union[str, property]] = None\n", | |
"\n", | |
" def __post_init__(self):\n", | |
" print('__post_init__')\n", | |
" if self.name is None:\n", | |
" self.name = 'Barack'\n", | |
"\n", | |
" def _get_name(self) -> Optional[str]:\n", | |
" print('getter:', self._name)\n", | |
" return self._name\n", | |
"\n", | |
" def _set_name(self, name: str):\n", | |
" print('setter:', name)\n", | |
" self._name = name\n", | |
"\n", | |
"# Note that this must be a module-level call.\n", | |
"Test2.name = property(Test2._get_name, Test2._set_name)\n", | |
"\n", | |
"t = Test2(name=None)\n", | |
"print('name:', t.name)\n", | |
"t.name = 'Donald'\n", | |
"print('name:', t.name)\n", | |
"t.name = 'Joe'\n", | |
"print('name:', t.name)\n", | |
"print(vars(t))\n", | |
"print('-'*30)\n", | |
"t = Test2(name='George')\n", | |
"print('name:', t.name)\n", | |
"print(vars(t))" | |
] | |
} | |
], | |
"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.8.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment