From 93615c8491d3665886a52685a333389fa40abda5 Mon Sep 17 00:00:00 2001 From: JuanBC Date: Wed, 14 Jul 2021 00:15:59 -0300 Subject: [PATCH] end 0.5 --- README.md | 9 +- docs/source/tutorial.ipynb | 481 +++++++++++++++---------------------- uttr.py | 3 +- 3 files changed, 204 insertions(+), 289 deletions(-) diff --git a/README.md b/README.md index 1ea048d..aa30226 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,11 @@ -**uttrs** is mainly two functions: +**uttrs** is mainly three functions: - `uttr.ib` which generates attributes sensitive to units. - `uttr.array_accessor` which allows access to attributes linked to units, and transform them into numpy arrays. +- `uttr.s` a class decorator to automatically add the `array_accessor`. ## Code and issues @@ -104,12 +105,11 @@ Suggested units in the information of the attributes behave like this: - If, otherwise, another unit is used as input, it is validated the feasibility of the conversion to default unit. ```python -import attr import uttr import astropy.units as u -@attr.s +@uttr.s class Galaxy: x = uttr.ib(unit=u.kpc) y = uttr.ib(unit=u.kpc) @@ -121,9 +121,8 @@ class Galaxy: m = uttr.ib(unit=u.M_sun) - notes = attr.ib(validator=attr.validators.instance_of(str)) + notes = uttr.ib(converter=str) - arr_ = uttr.array_accessor() ``` ## Creating a galaxy diff --git a/docs/source/tutorial.ipynb b/docs/source/tutorial.ipynb index ada6ef1..2a10e10 100644 --- a/docs/source/tutorial.ipynb +++ b/docs/source/tutorial.ipynb @@ -2,13 +2,11 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, "source": [ "# Tutorial\n", "\n", "This tutorial is intended to serve as a guide on how to create classes using *uttrs*\n", "\n", - "Warning: This is still a DRAFT version.\n", "\n", "## Interactive Version\n", "\n", @@ -27,22 +25,22 @@ "\n", "> Note: This tutorial assumes knowledge on the aforementioned libraries.\n", "Please refer to the reference links at the end of this notebook for more information." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, - "outputs": [], "source": [ - "import attr\n", + "import attr # to use the .validators module\n", "import uttr\n", "import astropy.units as u" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "### The Galaxy Class\n", "\n", @@ -56,15 +54,14 @@ "* `m`: Masses of the particles in units of solar masses ($M_\\odot$).\n", "\n", "The last attribute `notes` is a description text about the galaxy and can be implemented with the standar *attrs* library." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 2, - "metadata": {}, - "outputs": [], "source": [ - "@attr.s\n", + "@uttr.s\n", "class Galaxy:\n", " x = uttr.ib(unit=u.kpc)\n", " y = uttr.ib(unit=u.kpc)\n", @@ -77,11 +74,12 @@ " m = uttr.ib(unit=u.M_sun)\n", "\n", " notes = attr.ib(validator=attr.validators.instance_of(str))" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "### Galaxy with Default Units\n", "\n", @@ -92,13 +90,12 @@ "Part of *uttrs* power is its ability to assign default units when not provided, or to validate that the input unit is physically compatible with the given default.\n", "\n", "Let's see first an example in which all units are assigned automatically." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 3, - "metadata": {}, - "outputs": [], "source": [ "gal = Galaxy(\n", " x=[1, 1, 3, 4],\n", @@ -110,130 +107,131 @@ " m=[200, 100, 20, 5],\n", " notes=\"A random galaxy with arbitrary numbers.\",\n", ")" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "Let's verify that all attributes of the class were given the correct units." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "source": [ + "gal.x" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[1,~1,~3,~4] \\; \\mathrm{kpc}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[1,~1,~3,~4] \\; \\mathrm{kpc}$" ] }, - "execution_count": 4, "metadata": {}, - "output_type": "execute_result" + "execution_count": 4 } ], - "source": [ - "gal.x" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "source": [ + "gal.y" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[10,~2,~3,~100] \\; \\mathrm{kpc}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[10,~2,~3,~100] \\; \\mathrm{kpc}$" ] }, - "execution_count": 5, "metadata": {}, - "output_type": "execute_result" + "execution_count": 5 } ], - "source": [ - "gal.y" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "source": [ + "gal.vx" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[1000,~1023,~2346,~1334] \\; \\mathrm{\\frac{km}{s}}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[1000,~1023,~2346,~1334] \\; \\mathrm{\\frac{km}{s}}$" ] }, - "execution_count": 6, "metadata": {}, - "output_type": "execute_result" + "execution_count": 6 } ], - "source": [ - "gal.vx" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "source": [ + "gal.m" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[200,~100,~20,~5] \\; \\mathrm{M_{\\odot}}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[200,~100,~20,~5] \\; \\mathrm{M_{\\odot}}$" ] }, - "execution_count": 7, "metadata": {}, - "output_type": "execute_result" + "execution_count": 7 } ], - "source": [ - "gal.m" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 8, - "metadata": {}, + "source": [ + "gal.notes" + ], "outputs": [ { + "output_type": "execute_result", "data": { "text/plain": [ "'A random galaxy with arbitrary numbers.'" ] }, - "execution_count": 8, "metadata": {}, - "output_type": "execute_result" + "execution_count": 8 } ], - "source": [ - "gal.notes" - ] + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "### Galaxy with Explicit Units\n", "\n", @@ -241,13 +239,12 @@ "In this case, we have to be mindful of the phyisical equivalence of units with the ones given at the time the class was created.\n", "\n", "For example, we could suggest that the dimension `z` be given in parsecs, `vy` in $km/h$ and masses in $kg$." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 9, - "metadata": {}, - "outputs": [], "source": [ "gal = Galaxy(\n", " x=[1, 1, 3, 4],\n", @@ -259,130 +256,143 @@ " m=[200, 100, 20, 5] * u.kg,\n", " notes=\"A random galaxy with arbitrary numbers.\",\n", ")" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "As we note above, this works as expected without error.\n", "We can further access any of the attributes and verify that they keep the suggested units." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 10, - "metadata": {}, + "source": [ + "gal.z # parsecs" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[1000,~1000,~1000,~1000] \\; \\mathrm{pc}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[1000,~1000,~1000,~1000] \\; \\mathrm{pc}$" ] }, - "execution_count": 10, "metadata": {}, - "output_type": "execute_result" + "execution_count": 10 } ], - "source": [ - "gal.z # parsecs" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "source": [ + "gal.m # kg" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[200,~100,~20,~5] \\; \\mathrm{kg}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[200,~100,~20,~5] \\; \\mathrm{kg}$" ] }, - "execution_count": 11, "metadata": {}, - "output_type": "execute_result" + "execution_count": 11 } ], - "source": [ - "gal.m # kg" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 12, - "metadata": {}, + "source": [ + "gal.vx # default km/s" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[1000,~1023,~2346,~1334] \\; \\mathrm{\\frac{km}{s}}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[1000,~1023,~2346,~1334] \\; \\mathrm{\\frac{km}{s}}$" ] }, - "execution_count": 12, "metadata": {}, - "output_type": "execute_result" + "execution_count": 12 } ], - "source": [ - "gal.vx # default km/s" - ] + "metadata": {} }, { "cell_type": "code", "execution_count": 13, - "metadata": {}, + "source": [ + "gal.vy # km/h" + ], "outputs": [ { + "output_type": "execute_result", "data": { - "text/latex": [ - "$[9956,~833,~954,~1024] \\; \\mathrm{\\frac{km}{h}}$" - ], "text/plain": [ "" + ], + "text/latex": [ + "$[9956,~833,~954,~1024] \\; \\mathrm{\\frac{km}{h}}$" ] }, - "execution_count": 13, "metadata": {}, - "output_type": "execute_result" + "execution_count": 13 } ], - "source": [ - "gal.vy # km/h" - ] + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "On the other hand, if we try to input a unit that is incompatible with the suggested input unit, a `ValueError` exception is raised.\n", "\n", "To show this, let's try to assign `x` values with units of grams ($g$)." - ] + ], + "metadata": {} }, { "cell_type": "code", "execution_count": 14, - "metadata": {}, + "source": [ + "gal = Galaxy(\n", + " x=[1, 1, 3, 4] * u.g,\n", + " y=[10, 2, 3, 100],\n", + " z=[1000, 1000, 1000, 1000] * u.parsec,\n", + " vx=[1000, 1023, 2346, 1334],\n", + " vy=[9956, 833, 954, 1024] * (u.km / u.h),\n", + " vz=[1253, 956, 1054, 3568],\n", + " m=[200, 100, 20, 5] * u.kg,\n", + " notes=\"A random galaxy with arbitrary numbers.\",\n", + ")" + ], "outputs": [ { + "output_type": "error", "ename": "ValueError", "evalue": "Unit of attribute 'x' must be equivalent to 'kpc'. Found 'g'.", - "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnitConversionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mvalidate_is_equivalent_unit\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0munity\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\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 137\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mvalidate_is_equivalent_unit\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0munity\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\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 133\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/quantity.py\u001b[0m in \u001b[0;36mto\u001b[0;34m(self, unit, equivalencies)\u001b[0m\n\u001b[1;32m 688\u001b[0m \u001b[0munit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mUnit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 689\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_new_view\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_to_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mequivalencies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munit\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 690\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/quantity.py\u001b[0m in \u001b[0;36m_to_value\u001b[0;34m(self, unit, equivalencies)\u001b[0m\n\u001b[1;32m 659\u001b[0m \u001b[0mequivalencies\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_equivalencies\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 660\u001b[0;31m return self.unit.to(unit, self.view(np.ndarray),\n\u001b[0m\u001b[1;32m 661\u001b[0m equivalencies=equivalencies)\n", "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/astropy/units/core.py\u001b[0m in \u001b[0;36mto\u001b[0;34m(self, other, value, equivalencies)\u001b[0m\n\u001b[1;32m 986\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 987\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_converter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mequivalencies\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mequivalencies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\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 988\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", @@ -395,27 +405,15 @@ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m gal = Galaxy(\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mg\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[0my\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\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[1;32m 4\u001b[0m \u001b[0mz\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1000\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparsec\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1023\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2346\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1334\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\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, x, y, z, vx, vy, vz, m, notes)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnotes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnotes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m_config\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_run_validators\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0m__attr_validator_x\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\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 12\u001b[0m \u001b[0m__attr_validator_y\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_y\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0m__attr_validator_z\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m__attr_z\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/proyectos/uttrs/lib/python3.8/site-packages/attr/_make.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, inst, attr, value)\u001b[0m\n\u001b[1;32m 2721\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minst\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\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[1;32m 2722\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_validators\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2723\u001b[0;31m \u001b[0mv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minst\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\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 2724\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2725\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mvalidate_is_equivalent_unit\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 137\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mufound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattribute\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 139\u001b[0;31m raise ValueError(\n\u001b[0m\u001b[1;32m 140\u001b[0m \u001b[0;34mf\"Unit of attribute '{aname}' must be equivalent to '{unit}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[0;34mf\" Found '{ufound}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36mvalidate_is_equivalent_unit\u001b[0;34m(self, instance, attribute, value)\u001b[0m\n\u001b[1;32m 133\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUnitConversionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mufound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattribute\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munit\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 135\u001b[0;31m raise ValueError(\n\u001b[0m\u001b[1;32m 136\u001b[0m \u001b[0;34mf\"Unit of attribute '{aname}' must be equivalent to '{unit}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 137\u001b[0m \u001b[0;34mf\" Found '{ufound}'.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: Unit of attribute 'x' must be equivalent to 'kpc'. Found 'g'." ] } ], - "source": [ - "gal = Galaxy(\n", - " x=[1, 1, 3, 4] * u.g,\n", - " y=[10, 2, 3, 100],\n", - " z=[1000, 1000, 1000, 1000] * u.parsec,\n", - " vx=[1000, 1023, 2346, 1334],\n", - " vy=[9956, 833, 954, 1024] * (u.km / u.h),\n", - " vz=[1253, 956, 1054, 3568],\n", - " m=[200, 100, 20, 5] * u.kg,\n", - " notes=\"A random galaxy with arbitrary numbers.\",\n", - ")" - ] + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "## Automatic Cohersion of Units: Array Accessor\n", "\n", @@ -424,19 +422,19 @@ "This is achieved using the `uttr.array_accessor()` function.\n", "This allows for uniform access of attributes defined by uttrs, in a data structure that has faster access time than its counterpart with units.\n", "\n", - "To add this feature we must add an extra attribute to the class.\n", - "We suggest the name `arr_` for the accessor attribute.\n", + "By default the `@uttr.s` automataclly add an array accessor to decorated class. You can disabled this functionallity using the decorator like\n", + "`@uttr.s(aaccessor=None)`, or change the name of the property with `@uttr.s(aaccessor=\"other_name\")`.\n", + "\n", "\n", "Expanding on the previous example:" - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], + "execution_count": null, "source": [ - "@attr.s\n", + "@uttr.s\n", "class Galaxy:\n", " x = uttr.ib(unit=u.kpc)\n", " y = uttr.ib(unit=u.kpc)\n", @@ -448,23 +446,21 @@ "\n", " m = uttr.ib(unit=u.M_sun)\n", "\n", - " notes = attr.ib(validator=attr.validators.instance_of(str))\n", - "\n", - " arr_ = uttr.array_accessor() # the accessor" - ] + " notes = attr.ib(validator=attr.validators.instance_of(str))" + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "Let's instantiate the class again with some parameters with custom units." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], + "execution_count": null, "source": [ "gal = Galaxy(\n", " x=[1, 1, 3, 4],\n", @@ -476,141 +472,85 @@ " m=[200, 100, 20, 5] * u.kg,\n", " notes=\"A random galaxy with arbitrary numbers.\",\n", ")" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "If we now access `z` through our `arr_` accessor, *uttrs* will convert the values in parsec units to kiloparsecs and return a uniform numpy array." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 1., 1., 1.])" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "gal.arr_.z" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "While `z` keeps its original unit." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 1., 1., 1.])" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "gal.arr_.z" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "The same applies to `vy` and `m`." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.00582884e-28, 5.02914422e-29, 1.00582884e-29, 2.51457211e-30])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "gal.arr_.m" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([2.76555556, 0.23138889, 0.265 , 0.28444444])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "gal.arr_.vy" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "If we try to access a private attribute not from `uttr.ib`, an `AttributeError` exception is raised." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "No uttr.Attribute 'notes'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mgal\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marr_\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnotes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/proyectos/uttrs/src/uttr.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, a)\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0marr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 279\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 280\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mAttributeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"No uttr.Attribute '{a}'\"\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 281\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 282\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: No uttr.Attribute 'notes'" - ] - } - ], + "execution_count": null, "source": [ "gal.arr_.notes" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "## Using the `array_accessor`\n", "\n", @@ -621,15 +561,14 @@ "As a helper, `array_accesor` will perform the transformation in a transparent way to the user, avoiding the need to replicate information regarding units.\n", "\n", "For example, if we wanted to program code that generates a new Galaxy object with a single particle that is the average mean of all the rest, we could do something like this:" - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], + "execution_count": null, "source": [ - "@attr.s\n", + "@uttr.s\n", "class Galaxy:\n", " x = uttr.ib(unit=u.kpc)\n", " y = uttr.ib(unit=u.kpc)\n", @@ -643,8 +582,6 @@ "\n", " notes = attr.ib(validator=attr.validators.instance_of(str))\n", "\n", - " arr_ = uttr.array_accessor() # el accessor\n", - "\n", " def mean(self):\n", " x = np.mean(self.arr_.x)\n", " y = np.mean(self.arr_.y)\n", @@ -659,20 +596,20 @@ " return Galaxy(\n", " x=x, y=y, z=z, vx=vx, vy=vy, vz=vz, m=m, notes=self.notes\n", " )" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "We could now create a galaxy with 1 million random elements and calculate the \"average\" galaxy." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], + "execution_count": null, "source": [ "import numpy as np\n", "\n", @@ -691,42 +628,31 @@ " m=random.random(size=size) * u.kg,\n", " notes=\"A random galaxy with arbitrary numbers.\",\n", ")" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Galaxy(x=, y=, z=, vx=, vy=, vz=, m=, notes='A random galaxy with arbitrary numbers.')" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "gal.mean()" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "markdown", - "metadata": {}, "source": [ "To complete the example, let's see how would a `mean` method look like without `array_accessor`." - ] + ], + "metadata": {} }, { "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], + "execution_count": null, "source": [ - "@attr.s\n", + "@uttr.s(aaccessor=None)\n", "class Galaxy:\n", " x = uttr.ib(unit=u.kpc)\n", " y = uttr.ib(unit=u.kpc)\n", @@ -740,8 +666,6 @@ "\n", " notes = attr.ib(validator=attr.validators.instance_of(str))\n", "\n", - " arr_ = uttr.array_accessor() # el accessor\n", - "\n", " def mean(self):\n", " x = np.mean(self.x.to_value(u.kpc))\n", " y = np.mean(self.y.to_value(u.kpc))\n", @@ -756,35 +680,26 @@ " return Galaxy(\n", " x=x, y=y, z=z, vx=vx, vy=vy, vz=vz, m=m, notes=self.notes\n", " )" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2020-11-24'" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, "source": [ "import datetime as dt\n", "dt.date.today().isoformat()" - ] + ], + "outputs": [], + "metadata": {} }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "source": [], "outputs": [], - "source": [] + "metadata": {} } ], "metadata": { @@ -808,4 +723,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/uttr.py b/uttr.py index b2f50d3..8689552 100644 --- a/uttr.py +++ b/uttr.py @@ -18,6 +18,7 @@ - ``uttr.ib`` which generates attributes sensitive to units. - ``uttr.array_accessor`` which allows access to attributes linked to units, and transform them into numpy arrays. +- ``uttr.s`` a class decorator to automatically add the `array_accessor`. References @@ -41,7 +42,7 @@ # METADATA # ============================================================================= -__all__ = ["attribute", "ib", "array_accessor", "s"] +__all__ = ["s", "ib", "attribute", "array_accessor"] __version__ = "0.5"