From bfdb76b448b2fec2079f90d1f153f77b68dd777e Mon Sep 17 00:00:00 2001 From: stamper <tbyhdgs@gmail.com> Date: Tue, 7 May 2019 10:53:19 +0200 Subject: [PATCH] add material from dataclasses session. --- .gitignore | 1 + .../dataclasses-complete.ipynb | 563 +++++++++++++++ .../2019-04-30-dataclasses/dataclasses.ipynb | 657 ++++++++++++++++++ meetings/{ => old}/ete.rst | 0 meetings/{ => old}/schedule.txt | 0 5 files changed, 1221 insertions(+) create mode 100644 .gitignore create mode 100644 meetings/2019/2019-04-30-dataclasses/dataclasses-complete.ipynb create mode 100644 meetings/2019/2019-04-30-dataclasses/dataclasses.ipynb rename meetings/{ => old}/ete.rst (100%) rename meetings/{ => old}/schedule.txt (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b64427c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*ipynb_checkpoints* diff --git a/meetings/2019/2019-04-30-dataclasses/dataclasses-complete.ipynb b/meetings/2019/2019-04-30-dataclasses/dataclasses-complete.ipynb new file mode 100644 index 0000000..310069a --- /dev/null +++ b/meetings/2019/2019-04-30-dataclasses/dataclasses-complete.ipynb @@ -0,0 +1,563 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# EMBL Python User Group\n", + "## 2019-04-30 `dataclasses`\n", + "\n", + "__Toby Hodges__ ([toby.hodges@embl.de](mailto:toby.hodges@embl.de))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Material largely based on [this tutorial blogpost from RealPython](https://realpython.com/python-data-classes/)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float\n", + " lat: float" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "here = Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Location" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(here)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Location(name='Heidelberg', lon=8.672434, lat=49.39875)\n" + ] + } + ], + "source": [ + "print(here)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class normalLocation:\n", + " def __init__(self, name, lat, lon):\n", + " self.name = name\n", + " self.lat = lat\n", + " self.lon = lon" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "there = normalLocation(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.normalLocation" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(there)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<__main__.normalLocation object at 0x112953ef0>\n" + ] + } + ], + "source": [ + "print(there)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Note__: think decorators are weird? No worries - you can also import the `dataclasses.make_dataclass` function and create your dataclass with that instead of using the `@dataclass`+`class` approach." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "there == normalLocation(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "here == Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Type Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float\n", + " lat: float" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Any\n", + "@dataclass\n", + "class permissiveLocation:\n", + " name: str\n", + " lon: Any\n", + " lat: Any" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "nonsense = permissiveLocation(name=\"Alderaan\", lat=\"a long time ago\", lon=[\"a\", \"galaxy\", \"far\", \"far\", \"away\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "permissiveLocation(name='Alderaan', lon=['a', 'galaxy', 'far', 'far', 'away'], lat='a long time ago')\n" + ] + } + ], + "source": [ + "print(nonsense)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Defaults" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float=0.0\n", + " lat: float=0.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Methods are defined in the same way as usual." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "from math import asin, cos, radians, sin, sqrt\n", + "\n", + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float=0.0\n", + " lat: float=0.0\n", + " def distance_to(self, other):\n", + " r = 6371 # Earth radius in kilometers\n", + " lam_1, lam_2 = radians(self.lon), radians(other.lon)\n", + " phi_1, phi_2 = radians(self.lat), radians(other.lat)\n", + " h = (sin((phi_2 - phi_1) / 2)**2\n", + " + cos(phi_1) * cos(phi_2) * sin((lam_2 - lam_1) / 2)**2)\n", + " return 2 * r * asin(sqrt(h))" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "here = Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)\n", + "there = Location(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9215.974289879121" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "here.distance_to(there)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Inheritance" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Capital(Location):\n", + " country: str=\"undefined\"" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "capital_of_germany = Capital(\"berlin\", lat=52.520008, lon=13.404954, country=\"Germany\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Fields" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "@dataclass\n", + "class PlayingCard:\n", + " rank: str\n", + " suit: str\n", + "\n", + "@dataclass\n", + "class Deck:\n", + " cards: List[PlayingCard]\n", + "\n", + "RANKS = '2 3 4 5 6 7 8 9 10 J Q K A'.split()\n", + "SUITS = '♣ ♢ ♡ ♠'.split()\n", + "def make_french_deck():\n", + " return [PlayingCard(r, s) for s in SUITS for r in RANKS]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(See [here](https://en.wikipedia.org/wiki/Unicode_input) for more about using Unicode input...)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[PlayingCard(rank='2', suit='♣'),\n", + " PlayingCard(rank='3', suit='♣'),\n", + " PlayingCard(rank='4', suit='♣'),\n", + " PlayingCard(rank='5', suit='♣'),\n", + " PlayingCard(rank='6', suit='♣'),\n", + " PlayingCard(rank='7', suit='♣'),\n", + " PlayingCard(rank='8', suit='♣'),\n", + " PlayingCard(rank='9', suit='♣'),\n", + " PlayingCard(rank='10', suit='♣'),\n", + " PlayingCard(rank='J', suit='♣'),\n", + " PlayingCard(rank='Q', suit='♣'),\n", + " PlayingCard(rank='K', suit='♣'),\n", + " PlayingCard(rank='A', suit='♣'),\n", + " PlayingCard(rank='2', suit='♢'),\n", + " PlayingCard(rank='3', suit='♢'),\n", + " PlayingCard(rank='4', suit='♢'),\n", + " PlayingCard(rank='5', suit='♢'),\n", + " PlayingCard(rank='6', suit='♢'),\n", + " PlayingCard(rank='7', suit='♢'),\n", + " PlayingCard(rank='8', suit='♢'),\n", + " PlayingCard(rank='9', suit='♢'),\n", + " PlayingCard(rank='10', suit='♢'),\n", + " PlayingCard(rank='J', suit='♢'),\n", + " PlayingCard(rank='Q', suit='♢'),\n", + " PlayingCard(rank='K', suit='♢'),\n", + " PlayingCard(rank='A', suit='♢'),\n", + " PlayingCard(rank='2', suit='♡'),\n", + " PlayingCard(rank='3', suit='♡'),\n", + " PlayingCard(rank='4', suit='♡'),\n", + " PlayingCard(rank='5', suit='♡'),\n", + " PlayingCard(rank='6', suit='♡'),\n", + " PlayingCard(rank='7', suit='♡'),\n", + " PlayingCard(rank='8', suit='♡'),\n", + " PlayingCard(rank='9', suit='♡'),\n", + " PlayingCard(rank='10', suit='♡'),\n", + " PlayingCard(rank='J', suit='♡'),\n", + " PlayingCard(rank='Q', suit='♡'),\n", + " PlayingCard(rank='K', suit='♡'),\n", + " PlayingCard(rank='A', suit='♡'),\n", + " PlayingCard(rank='2', suit='♠'),\n", + " PlayingCard(rank='3', suit='♠'),\n", + " PlayingCard(rank='4', suit='♠'),\n", + " PlayingCard(rank='5', suit='♠'),\n", + " PlayingCard(rank='6', suit='♠'),\n", + " PlayingCard(rank='7', suit='♠'),\n", + " PlayingCard(rank='8', suit='♠'),\n", + " PlayingCard(rank='9', suit='♠'),\n", + " PlayingCard(rank='10', suit='♠'),\n", + " PlayingCard(rank='J', suit='♠'),\n", + " PlayingCard(rank='Q', suit='♠'),\n", + " PlayingCard(rank='K', suit='♠'),\n", + " PlayingCard(rank='A', suit='♠')]" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "make_french_deck()" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "mutable default <class 'list'> for field cards is not allowed: use default_factory", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-50-c0e6c8958259>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m@\u001b[0m\u001b[0mdataclass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mDeck\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mcards\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mList\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mPlayingCard\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmake_french_deck\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# here be dragons!\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36mdataclass\u001b[0;34m(_cls, init, repr, eq, order, unsafe_hash, frozen)\u001b[0m\n\u001b[1;32m 989\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 990\u001b[0m \u001b[0;31m# We're called as @dataclass without parens.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 991\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_cls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 992\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 993\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36mwrap\u001b[0;34m(cls)\u001b[0m\n\u001b[1;32m 981\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 982\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 983\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_process_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrepr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0meq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munsafe_hash\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfrozen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 984\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 985\u001b[0m \u001b[0;31m# See if we're being called as @dataclass or @dataclass().\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m_process_class\u001b[0;34m(cls, init, repr, eq, order, unsafe_hash, frozen)\u001b[0m\n\u001b[1;32m 832\u001b[0m \u001b[0;31m# we can.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 833\u001b[0m cls_fields = [_get_field(cls, name, type)\n\u001b[0;32m--> 834\u001b[0;31m for name, type in cls_annotations.items()]\n\u001b[0m\u001b[1;32m 835\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcls_fields\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 836\u001b[0m \u001b[0mfields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 832\u001b[0m \u001b[0;31m# we can.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 833\u001b[0m cls_fields = [_get_field(cls, name, type)\n\u001b[0;32m--> 834\u001b[0;31m for name, type in cls_annotations.items()]\n\u001b[0m\u001b[1;32m 835\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcls_fields\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 836\u001b[0m \u001b[0mfields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m_get_field\u001b[0;34m(cls, a_name, a_type)\u001b[0m\n\u001b[1;32m 725\u001b[0m \u001b[0;31m# For real fields, disallow mutable defaults for known types.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 726\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_field_type\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0m_FIELD\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefault\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 727\u001b[0;31m raise ValueError(f'mutable default {type(f.default)} for field '\n\u001b[0m\u001b[1;32m 728\u001b[0m f'{f.name} is not allowed: use default_factory')\n\u001b[1;32m 729\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: mutable default <class 'list'> for field cards is not allowed: use default_factory" + ] + } + ], + "source": [ + "@dataclass\n", + "class Deck:\n", + " cards: List[PlayingCard] = make_french_deck() # here be dragons!" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import field\n", + "@dataclass\n", + "class Deck: # Will NOT work\n", + " cards: List[PlayingCard] = field(default_factory=make_french_deck)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [], + "source": [ + "deck = Deck()" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), PlayingCard(rank='4', suit='♣'), PlayingCard(rank='5', suit='♣'), PlayingCard(rank='6', suit='♣'), PlayingCard(rank='7', suit='♣'), PlayingCard(rank='8', suit='♣'), PlayingCard(rank='9', suit='♣'), PlayingCard(rank='10', suit='♣'), PlayingCard(rank='J', suit='♣'), PlayingCard(rank='Q', suit='♣'), PlayingCard(rank='K', suit='♣'), PlayingCard(rank='A', suit='♣'), PlayingCard(rank='2', suit='♢'), PlayingCard(rank='3', suit='♢'), PlayingCard(rank='4', suit='♢'), PlayingCard(rank='5', suit='♢'), PlayingCard(rank='6', suit='♢'), PlayingCard(rank='7', suit='♢'), PlayingCard(rank='8', suit='♢'), PlayingCard(rank='9', suit='♢'), PlayingCard(rank='10', suit='♢'), PlayingCard(rank='J', suit='♢'), PlayingCard(rank='Q', suit='♢'), PlayingCard(rank='K', suit='♢'), PlayingCard(rank='A', suit='♢'), PlayingCard(rank='2', suit='♡'), PlayingCard(rank='3', suit='♡'), PlayingCard(rank='4', suit='♡'), PlayingCard(rank='5', suit='♡'), PlayingCard(rank='6', suit='♡'), PlayingCard(rank='7', suit='♡'), PlayingCard(rank='8', suit='♡'), PlayingCard(rank='9', suit='♡'), PlayingCard(rank='10', suit='♡'), PlayingCard(rank='J', suit='♡'), PlayingCard(rank='Q', suit='♡'), PlayingCard(rank='K', suit='♡'), PlayingCard(rank='A', suit='♡'), PlayingCard(rank='2', suit='♠'), PlayingCard(rank='3', suit='♠'), PlayingCard(rank='4', suit='♠'), PlayingCard(rank='5', suit='♠'), PlayingCard(rank='6', suit='♠'), PlayingCard(rank='7', suit='♠'), PlayingCard(rank='8', suit='♠'), PlayingCard(rank='9', suit='♠'), PlayingCard(rank='10', suit='♠'), PlayingCard(rank='J', suit='♠'), PlayingCard(rank='Q', suit='♠'), PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')]\n" + ] + } + ], + "source": [ + "print(deck.cards)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/meetings/2019/2019-04-30-dataclasses/dataclasses.ipynb b/meetings/2019/2019-04-30-dataclasses/dataclasses.ipynb new file mode 100644 index 0000000..714f760 --- /dev/null +++ b/meetings/2019/2019-04-30-dataclasses/dataclasses.ipynb @@ -0,0 +1,657 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# EMBL Python User Group\n", + "## 2019-04-30 `dataclasses`\n", + "\n", + "__Toby Hodges__ ([toby.hodges@embl.de](mailto:toby.hodges@embl.de))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Material largely based on [this tutorial blogpost from RealPython](https://realpython.com/python-data-classes/)." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import dataclass" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float\n", + " lat: float" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "here = Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "here = Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Location" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(here)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Location(name='Heidelberg', lon=8.672434, lat=49.39875)\n" + ] + } + ], + "source": [ + "print(here)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "class NormalLocation:\n", + " def __init__(self, name, lat, lon):\n", + " self.name = name\n", + " self.lat = lat\n", + " self.lon = lon" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "there = NormalLocation(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.NormalLocation" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(there)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<__main__.NormalLocation object at 0x112dc6eb8>\n" + ] + } + ], + "source": [ + "print(there)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Note__: think decorators are weird? No worries - you can also import the `dataclasses.make_dataclass` function and create your dataclass with that instead of using the `@dataclass`+`class` approach." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "there == normalLocation(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "here == Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Type Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float\n", + " lat: float" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Any\n", + "@dataclass\n", + "class permissiveLocation:\n", + " name: str\n", + " lon: Any\n", + " lat: Any" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "nonsense = permissiveLocation(name=\"Alderaan\", lat=\"a long time ago\", lon=[\"a\", \"galaxy\", \"far\", \"far\", \"away\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "permissiveLocation(name='Alderaan', lon=['a', 'galaxy', 'far', 'far', 'away'], lat='a long time ago')\n" + ] + } + ], + "source": [ + "print(nonsense)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Defaults" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float=0.0\n", + " lat: float=0.0" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [], + "source": [ + "def mulitplier(a, b, c, d):\n", + " return a*b*c*d" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": {}, + "outputs": [], + "source": [ + "def mulitplier(a, b, c, d=1):\n", + " return a*b*c*d" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "non-default argument follows default argument (<ipython-input-76-2de8e556e2da>, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"<ipython-input-76-2de8e556e2da>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m def mulitplier(a=1, b, c, d):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m non-default argument follows default argument\n" + ] + } + ], + "source": [ + "def mulitplier(a=1, b, c, d):\n", + " return a*b*c*d" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Location(name='somewhere near the middle of the atlantic', lon=0.0, lat=0.0)\n" + ] + } + ], + "source": [ + "default_loc = Location(\"somewhere near the middle of the atlantic\")\n", + "print(default_loc)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Methods are defined in the same way as usual." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "from math import asin, cos, radians, sin, sqrt\n", + "\n", + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float=0.0\n", + " lat: float=0.0\n", + " def distance_to(self, other):\n", + " r = 6371 # Earth radius in kilometers\n", + " lam_1, lam_2 = radians(self.lon), radians(other.lon)\n", + " phi_1, phi_2 = radians(self.lat), radians(other.lat)\n", + " h = (sin((phi_2 - phi_1) / 2)**2\n", + " + cos(phi_1) * cos(phi_2) * sin((lam_2 - lam_1) / 2)**2)\n", + " return 2 * r * asin(sqrt(h))" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [], + "source": [ + "here = Location(name=\"Heidelberg\", lat=49.398750, lon=8.672434)\n", + "there = Location(name=\"Redwood City\", lat=37.484779, lon=-122.228149)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9215.974289879121" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "here.distance_to(there)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Inheritance" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "@dataclass\n", + "class Location:\n", + " name: str\n", + " lon: float=0.0\n", + " lat: float=0.0\n", + "\n", + "@dataclass\n", + "class Capital(Location):\n", + " country: str=\"undefined\"" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [], + "source": [ + "capital_of_germany = Capital(\"berlin\", lat=52.520008, lon=13.404954, country=\"Germany\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Fields" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "@dataclass\n", + "class PlayingCard:\n", + " rank: str\n", + " suit: str\n", + "\n", + "@dataclass\n", + "class Deck:\n", + " cards: List[PlayingCard]\n", + "\n", + "RANKS = '2 3 4 5 6 7 8 9 10 J Q K A'.split()\n", + "SUITS = '♣ ♢ ♡ ♠'.split()\n", + "def make_french_deck():\n", + " return [PlayingCard(r, s) for s in SUITS for r in RANKS]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(See [here](https://en.wikipedia.org/wiki/Unicode_input) for more about using Unicode input...)" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[PlayingCard(rank='2', suit='♣'),\n", + " PlayingCard(rank='3', suit='♣'),\n", + " PlayingCard(rank='4', suit='♣'),\n", + " PlayingCard(rank='5', suit='♣'),\n", + " PlayingCard(rank='6', suit='♣'),\n", + " PlayingCard(rank='7', suit='♣'),\n", + " PlayingCard(rank='8', suit='♣'),\n", + " PlayingCard(rank='9', suit='♣'),\n", + " PlayingCard(rank='10', suit='♣'),\n", + " PlayingCard(rank='J', suit='♣'),\n", + " PlayingCard(rank='Q', suit='♣'),\n", + " PlayingCard(rank='K', suit='♣'),\n", + " PlayingCard(rank='A', suit='♣'),\n", + " PlayingCard(rank='2', suit='♢'),\n", + " PlayingCard(rank='3', suit='♢'),\n", + " PlayingCard(rank='4', suit='♢'),\n", + " PlayingCard(rank='5', suit='♢'),\n", + " PlayingCard(rank='6', suit='♢'),\n", + " PlayingCard(rank='7', suit='♢'),\n", + " PlayingCard(rank='8', suit='♢'),\n", + " PlayingCard(rank='9', suit='♢'),\n", + " PlayingCard(rank='10', suit='♢'),\n", + " PlayingCard(rank='J', suit='♢'),\n", + " PlayingCard(rank='Q', suit='♢'),\n", + " PlayingCard(rank='K', suit='♢'),\n", + " PlayingCard(rank='A', suit='♢'),\n", + " PlayingCard(rank='2', suit='♡'),\n", + " PlayingCard(rank='3', suit='♡'),\n", + " PlayingCard(rank='4', suit='♡'),\n", + " PlayingCard(rank='5', suit='♡'),\n", + " PlayingCard(rank='6', suit='♡'),\n", + " PlayingCard(rank='7', suit='♡'),\n", + " PlayingCard(rank='8', suit='♡'),\n", + " PlayingCard(rank='9', suit='♡'),\n", + " PlayingCard(rank='10', suit='♡'),\n", + " PlayingCard(rank='J', suit='♡'),\n", + " PlayingCard(rank='Q', suit='♡'),\n", + " PlayingCard(rank='K', suit='♡'),\n", + " PlayingCard(rank='A', suit='♡'),\n", + " PlayingCard(rank='2', suit='♠'),\n", + " PlayingCard(rank='3', suit='♠'),\n", + " PlayingCard(rank='4', suit='♠'),\n", + " PlayingCard(rank='5', suit='♠'),\n", + " PlayingCard(rank='6', suit='♠'),\n", + " PlayingCard(rank='7', suit='♠'),\n", + " PlayingCard(rank='8', suit='♠'),\n", + " PlayingCard(rank='9', suit='♠'),\n", + " PlayingCard(rank='10', suit='♠'),\n", + " PlayingCard(rank='J', suit='♠'),\n", + " PlayingCard(rank='Q', suit='♠'),\n", + " PlayingCard(rank='K', suit='♠'),\n", + " PlayingCard(rank='A', suit='♠')]" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "make_french_deck()" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "mutable default <class 'list'> for field cards is not allowed: use default_factory", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-90-c0e6c8958259>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m@\u001b[0m\u001b[0mdataclass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mDeck\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mcards\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mList\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mPlayingCard\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmake_french_deck\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# here be dragons!\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36mdataclass\u001b[0;34m(_cls, init, repr, eq, order, unsafe_hash, frozen)\u001b[0m\n\u001b[1;32m 989\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 990\u001b[0m \u001b[0;31m# We're called as @dataclass without parens.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 991\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_cls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 992\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 993\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36mwrap\u001b[0;34m(cls)\u001b[0m\n\u001b[1;32m 981\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 982\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 983\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_process_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrepr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0meq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munsafe_hash\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfrozen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 984\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 985\u001b[0m \u001b[0;31m# See if we're being called as @dataclass or @dataclass().\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m_process_class\u001b[0;34m(cls, init, repr, eq, order, unsafe_hash, frozen)\u001b[0m\n\u001b[1;32m 832\u001b[0m \u001b[0;31m# we can.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 833\u001b[0m cls_fields = [_get_field(cls, name, type)\n\u001b[0;32m--> 834\u001b[0;31m for name, type in cls_annotations.items()]\n\u001b[0m\u001b[1;32m 835\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcls_fields\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 836\u001b[0m \u001b[0mfields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 832\u001b[0m \u001b[0;31m# we can.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 833\u001b[0m cls_fields = [_get_field(cls, name, type)\n\u001b[0;32m--> 834\u001b[0;31m for name, type in cls_annotations.items()]\n\u001b[0m\u001b[1;32m 835\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcls_fields\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 836\u001b[0m \u001b[0mfields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda2/envs/Py37/lib/python3.7/dataclasses.py\u001b[0m in \u001b[0;36m_get_field\u001b[0;34m(cls, a_name, a_type)\u001b[0m\n\u001b[1;32m 725\u001b[0m \u001b[0;31m# For real fields, disallow mutable defaults for known types.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 726\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_field_type\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0m_FIELD\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefault\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 727\u001b[0;31m raise ValueError(f'mutable default {type(f.default)} for field '\n\u001b[0m\u001b[1;32m 728\u001b[0m f'{f.name} is not allowed: use default_factory')\n\u001b[1;32m 729\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: mutable default <class 'list'> for field cards is not allowed: use default_factory" + ] + } + ], + "source": [ + "@dataclass(frozen=True)\n", + "class Deck:\n", + " cards: List[PlayingCard] = make_french_deck() # here be dragons!" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [], + "source": [ + "from dataclasses import field\n", + "@dataclass\n", + "class Deck:\n", + " cards: List[PlayingCard] = field(default_factory=make_french_deck)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [], + "source": [ + "deck = Deck()" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), PlayingCard(rank='4', suit='♣'), PlayingCard(rank='5', suit='♣'), PlayingCard(rank='6', suit='♣'), PlayingCard(rank='7', suit='♣'), PlayingCard(rank='8', suit='♣'), PlayingCard(rank='9', suit='♣'), PlayingCard(rank='10', suit='♣'), PlayingCard(rank='J', suit='♣'), PlayingCard(rank='Q', suit='♣'), PlayingCard(rank='K', suit='♣'), PlayingCard(rank='A', suit='♣'), PlayingCard(rank='2', suit='♢'), PlayingCard(rank='3', suit='♢'), PlayingCard(rank='4', suit='♢'), PlayingCard(rank='5', suit='♢'), PlayingCard(rank='6', suit='♢'), PlayingCard(rank='7', suit='♢'), PlayingCard(rank='8', suit='♢'), PlayingCard(rank='9', suit='♢'), PlayingCard(rank='10', suit='♢'), PlayingCard(rank='J', suit='♢'), PlayingCard(rank='Q', suit='♢'), PlayingCard(rank='K', suit='♢'), PlayingCard(rank='A', suit='♢'), PlayingCard(rank='2', suit='♡'), PlayingCard(rank='3', suit='♡'), PlayingCard(rank='4', suit='♡'), PlayingCard(rank='5', suit='♡'), PlayingCard(rank='6', suit='♡'), PlayingCard(rank='7', suit='♡'), PlayingCard(rank='8', suit='♡'), PlayingCard(rank='9', suit='♡'), PlayingCard(rank='10', suit='♡'), PlayingCard(rank='J', suit='♡'), PlayingCard(rank='Q', suit='♡'), PlayingCard(rank='K', suit='♡'), PlayingCard(rank='A', suit='♡'), PlayingCard(rank='2', suit='♠'), PlayingCard(rank='3', suit='♠'), PlayingCard(rank='4', suit='♠'), PlayingCard(rank='5', suit='♠'), PlayingCard(rank='6', suit='♠'), PlayingCard(rank='7', suit='♠'), PlayingCard(rank='8', suit='♠'), PlayingCard(rank='9', suit='♠'), PlayingCard(rank='10', suit='♠'), PlayingCard(rank='J', suit='♠'), PlayingCard(rank='Q', suit='♠'), PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')]\n" + ] + } + ], + "source": [ + "print(deck.cards)" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'attrs'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-94-680b1dd3aa84>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mattrs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'attrs'" + ] + } + ], + "source": [ + "class Location:\n", + " __slots__(name, lon, lat)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/meetings/ete.rst b/meetings/old/ete.rst similarity index 100% rename from meetings/ete.rst rename to meetings/old/ete.rst diff --git a/meetings/schedule.txt b/meetings/old/schedule.txt similarity index 100% rename from meetings/schedule.txt rename to meetings/old/schedule.txt -- GitLab