quantity


Namequantity JSON
Version 0.11.4 PyPI version JSON
download
home_pagehttps://github.com/mamrhein/quantity
SummaryUnit-safe computations with quantities (including money)
upload_time2023-01-20 16:25:27
maintainer
docs_urlhttps://pythonhosted.org/quantity/
authorMichael Amrhein
requires_python>=3.7
licenseBSD
keywords quantity quantities unit units money currency exchange
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            The package _quantity_ provides classes for unit-safe computations with
quantities, including money.

### Defining a quantity class

A **basic** type of quantity is declared just by sub-classing _Quantity_:

    >>> class Length(Quantity):
    ...     pass
    ...

But, as long as there is no unit defined for that class, you can not create
any instance for the new quantity class:

    >>> l = Length(1)
    Traceback (most recent call last):
    ValueError: A unit must be given.

If there is a reference unit, the simplest way to define it is giving a name
and a symbol for it as keywords. The meta-class of _Quantity_ will
then create a unit automatically:

    >>> class Mass(Quantity,
    ...            ref_unit_name='Kilogram',
    ...            ref_unit_symbol='kg'):
    ...     pass
    ...
    >>> Mass.ref_unit
    Unit('kg')
    >>> class Length(Quantity,
    ...              ref_unit_name='Metre',
    ...              ref_unit_symbol='m'):
    ...     pass
    ...
    >>> Length.ref_unit
    Unit('m')

Now, this unit can be given to create a quantity:

    >>> METRE = Length.ref_unit
    >>> print(Length(15, METRE))
    15 m

If no unit is given, the reference unit is used:

    >>> print(Length(15))
    15 m

Other units can be derived from the reference unit (or another unit), giving
a definition by multiplying a scaling factor with that unit:

    >>> a_thousandth = Decimal("0.001")
    >>> KILOGRAM = Mass.ref_unit
    >>> GRAM = Mass.new_unit('g', 'Gram', a_thousandth * KILOGRAM)
    >>> MILLIMETRE = Length.new_unit('mm', 'Millimetre', a_thousandth * METRE)
    >>> MILLIMETRE
    Unit('mm')
    >>> KILOMETRE = Length.new_unit('km', 'Kilometre', 1000 * METRE)
    >>> KILOMETRE
    Unit('km')
    >>> CENTIMETRE = Length.new_unit('cm', 'Centimetre', 10 * MILLIMETRE)
    >>> CENTIMETRE
    Unit('cm')

Instead of a number a SI prefix can be used as scaling factor. SI prefixes are
provided in a sub-module:

    >>> from quantity.si_prefixes import *
    >>> NANO.abbr, NANO.name, NANO.factor
    ('n', 'Nano', Decimal('0.000000001'))

    >>> NANOMETRE = Length.new_unit('nm', 'Nanometre', NANO * METRE)
    >>> NANOMETRE
    Unit('nm')

Using one unit as a reference and defining all other units by giving a
scaling factor is only possible if the units have the same scale. Otherwise,
units can just be instantiated without giving a definition:

    >>> class Temperature(Quantity):
    ...     pass
    ...
    >>> CELSIUS = Temperature.new_unit('°C', 'Degree Celsius')
    >>> FAHRENHEIT = Temperature.new_unit('°F', 'Degree Fahrenheit')
    >>> KELVIN = Temperature.new_unit('K', 'Kelvin')

**Derived** types of quantities are declared by giving a definition based on
more basic types of quantities:

    >>> class Volume(Quantity,
    ...              define_as=Length ** 3,
    ...              ref_unit_name='Cubic Metre'):
    ...     pass
    ...
    >>> class Duration(Quantity,
    ...                ref_unit_name='Second',
    ...                ref_unit_symbol='s'):
    ...     pass
    ...
    >>> class Velocity(Quantity,
    ...                define_as=Length / Duration,
    ...                ref_unit_name='Metre per Second'):
    ...     pass
    ...

If no symbol for the reference unit is given with the class declaration, a
symbol is generated from the definition, as long as all types of quantities
in that definition have a reference unit.

    >>> Volume.ref_unit.symbol
    'm³'
    >>> Velocity.ref_unit.symbol
    'm/s'

Other units have to be defined explicitly. This can be done either as shown
above or by deriving them from units of the base quantities:

    >>> CUBIC_CENTIMETRE = Volume.derive_unit_from(CENTIMETRE,
    ...                                            name='Cubic Centimetre')
    >>> CUBIC_CENTIMETRE
    Unit('cm³')
    >>> HOUR = Duration.new_unit('h', 'Hour', 3600 * Duration.ref_unit)
    >>> KILOMETRE_PER_HOUR = Velocity.derive_unit_from(KILOMETRE, HOUR)
    >>> KILOMETRE_PER_HOUR
    Unit('km/h')

### Instantiating quantities

The simplest way to create an instance of a class _Quantity_ subclass is to
call the class giving an amount and a unit. If the unit is omitted, the
quantity's reference unit is used (if one is defined):

    >>> Length(15, MILLIMETRE)
    Length(Decimal(15), Unit('mm'))

Alternatively, an amount and a unit can be multiplied:

    >>> 17.5 * KILOMETRE
    Length(Decimal('17.5'), Unit('km'))

Also, it's possible to create a _Quantity_ sub-class instance from a string
representation:

    >>> Length('17.5 km')
    Length(Decimal('17.5'), Unit('km'))

### Unit-safe computations

A quantity can be converted to a quantity using a different unit by calling
the method _Quantity.convert_:

    >>> l5cm = Length(Decimal(5), CENTIMETRE)
    >>> l5cm.convert(MILLIMETRE)
    Length(Decimal(50), Unit('mm'))
    >>> l5cm.convert(KILOMETRE)
    Length(Decimal('0.00005'), Unit('km'))

Quantities can be compared to other quantities using all comparison operators
defined for numbers. Different units are taken into account automatically, as
long as they are compatible, i.e. a conversion is available:

    >>> Length(27) <= Length(91)
    True
    >>> Length(27, METRE) <= Length(91, CENTIMETRE)
    False

Quantities can be added to or subtracted from other quantities …:

    >>> Length(27) + Length(9)
    Length(Decimal(36))
    >>> Length(27) - Length(91)
    Length(Decimal(-64))
    >>> Length(27) + Length(12, CENTIMETER)
    Length(Decimal('27.12'))
    >>> Length(12, CENTIMETER) + Length(17, METER)
    Length(Decimal('1712'), Length.Unit('cm'))

… as long as they are instances of the same quantity type:

    >>> Length(27) + Duration(9)
    Traceback (most recent call last):
    IncompatibleUnitsError: Can't add a 'Length' and a 'Duration'

Quantities can be multiplied or divided by scalars, preserving the unit:

    >>> 7.5 * Length(3, CENTIMETRE)
    Length(Decimal('22.5'), Unit('cm'))
    >>> Duration(66, MINUTE) / 11
    Duration(Decimal(6), Unit('min'))

Quantities can be multiplied or divided by other quantities …:

    >>> Length(15, METRE) / Duration(3, SECOND)
    Velocity(Decimal(5))

… as long as the resulting type of quantity is defined …:

    >>> Duration(4, SECOND) * Length(7)
    Traceback (most recent call last):
    UndefinedResultError: Undefined result: Duration * Length

… or the result is a scalar:

    >>> Duration(2, MINUTE) / Duration(50, SECOND)
    Decimal('2.4')

### Money

_Money_ is a special type of quantity. Its unit type is known as currency.

Money differs from physical quantities mainly in two aspects:

* Money amounts are discrete. For each currency there is a smallest fraction
  that can not be split further.

* The relation between different currencies is not fixed, instead, it varies
  over time.

The sub-package _quantity.money_ provides classes and functions to deal
with these specifics.

A currency must explicitly be registered as a unit for further use. The
easiest way to do this is to call _Money.register_currency_. The method
is backed by a database of currencies defined in ISO 4217. It takes the 
3-character ISO 4217 code as parameter.

_Money_ derives from _Quantity_, so all operations on quantities can also be
applied to instances of _Money_. But because there is no fixed relation
between currencies, there is no implicit conversion between money amounts of
different currencies. Resulting values are always quantized to the smallest
fraction defined with the currency.

A conversion factor between two currencies can be defined by using the
class _ExchangeRate_. It is given a unit currency (aka base currency), a unit
multiple, a term currency (aka price currency) and a term amount, i.e. the
amount in term currency equivalent to unit multiple in unit currency.

Multiplying an amount in some currency with an exchange rate with the same
currency as unit currency results in the equivalent amount in term currency.
Likewise, dividing an amount in some currency with an exchange rate with the
same currency as term currency results in the equivalent amount in unit
currency.

As _Money_ derives from _Quantity_, it can be combined with other quantities
in order to define a new quantity. This is, for example, useful for defining
prices per quantum.

For more details see the documentation provided with the source distribution
or [here](https://quantity.readthedocs.io/).

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/mamrhein/quantity",
    "name": "quantity",
    "maintainer": "",
    "docs_url": "https://pythonhosted.org/quantity/",
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "quantity quantities unit units money currency exchange",
    "author": "Michael Amrhein",
    "author_email": "michael@adrhinum.de",
    "download_url": "https://files.pythonhosted.org/packages/3e/04/314e1c315ed491c237c30fcebee466b2a3a4a0d86a4d4caf8f6935429dd5/quantity-0.11.4.tar.gz",
    "platform": "all",
    "description": "The package _quantity_ provides classes for unit-safe computations with\nquantities, including money.\n\n### Defining a quantity class\n\nA **basic** type of quantity is declared just by sub-classing _Quantity_:\n\n    >>> class Length(Quantity):\n    ...     pass\n    ...\n\nBut, as long as there is no unit defined for that class, you can not create\nany instance for the new quantity class:\n\n    >>> l = Length(1)\n    Traceback (most recent call last):\n    ValueError: A unit must be given.\n\nIf there is a reference unit, the simplest way to define it is giving a name\nand a symbol for it as keywords. The meta-class of _Quantity_ will\nthen create a unit automatically:\n\n    >>> class Mass(Quantity,\n    ...            ref_unit_name='Kilogram',\n    ...            ref_unit_symbol='kg'):\n    ...     pass\n    ...\n    >>> Mass.ref_unit\n    Unit('kg')\n    >>> class Length(Quantity,\n    ...              ref_unit_name='Metre',\n    ...              ref_unit_symbol='m'):\n    ...     pass\n    ...\n    >>> Length.ref_unit\n    Unit('m')\n\nNow, this unit can be given to create a quantity:\n\n    >>> METRE = Length.ref_unit\n    >>> print(Length(15, METRE))\n    15 m\n\nIf no unit is given, the reference unit is used:\n\n    >>> print(Length(15))\n    15 m\n\nOther units can be derived from the reference unit (or another unit), giving\na definition by multiplying a scaling factor with that unit:\n\n    >>> a_thousandth = Decimal(\"0.001\")\n    >>> KILOGRAM = Mass.ref_unit\n    >>> GRAM = Mass.new_unit('g', 'Gram', a_thousandth * KILOGRAM)\n    >>> MILLIMETRE = Length.new_unit('mm', 'Millimetre', a_thousandth * METRE)\n    >>> MILLIMETRE\n    Unit('mm')\n    >>> KILOMETRE = Length.new_unit('km', 'Kilometre', 1000 * METRE)\n    >>> KILOMETRE\n    Unit('km')\n    >>> CENTIMETRE = Length.new_unit('cm', 'Centimetre', 10 * MILLIMETRE)\n    >>> CENTIMETRE\n    Unit('cm')\n\nInstead of a number a SI prefix can be used as scaling factor. SI prefixes are\nprovided in a sub-module:\n\n    >>> from quantity.si_prefixes import *\n    >>> NANO.abbr, NANO.name, NANO.factor\n    ('n', 'Nano', Decimal('0.000000001'))\n\n    >>> NANOMETRE = Length.new_unit('nm', 'Nanometre', NANO * METRE)\n    >>> NANOMETRE\n    Unit('nm')\n\nUsing one unit as a reference and defining all other units by giving a\nscaling factor is only possible if the units have the same scale. Otherwise,\nunits can just be instantiated without giving a definition:\n\n    >>> class Temperature(Quantity):\n    ...     pass\n    ...\n    >>> CELSIUS = Temperature.new_unit('\u00b0C', 'Degree Celsius')\n    >>> FAHRENHEIT = Temperature.new_unit('\u00b0F', 'Degree Fahrenheit')\n    >>> KELVIN = Temperature.new_unit('K', 'Kelvin')\n\n**Derived** types of quantities are declared by giving a definition based on\nmore basic types of quantities:\n\n    >>> class Volume(Quantity,\n    ...              define_as=Length ** 3,\n    ...              ref_unit_name='Cubic Metre'):\n    ...     pass\n    ...\n    >>> class Duration(Quantity,\n    ...                ref_unit_name='Second',\n    ...                ref_unit_symbol='s'):\n    ...     pass\n    ...\n    >>> class Velocity(Quantity,\n    ...                define_as=Length / Duration,\n    ...                ref_unit_name='Metre per Second'):\n    ...     pass\n    ...\n\nIf no symbol for the reference unit is given with the class declaration, a\nsymbol is generated from the definition, as long as all types of quantities\nin that definition have a reference unit.\n\n    >>> Volume.ref_unit.symbol\n    'm\u00b3'\n    >>> Velocity.ref_unit.symbol\n    'm/s'\n\nOther units have to be defined explicitly. This can be done either as shown\nabove or by deriving them from units of the base quantities:\n\n    >>> CUBIC_CENTIMETRE = Volume.derive_unit_from(CENTIMETRE,\n    ...                                            name='Cubic Centimetre')\n    >>> CUBIC_CENTIMETRE\n    Unit('cm\u00b3')\n    >>> HOUR = Duration.new_unit('h', 'Hour', 3600 * Duration.ref_unit)\n    >>> KILOMETRE_PER_HOUR = Velocity.derive_unit_from(KILOMETRE, HOUR)\n    >>> KILOMETRE_PER_HOUR\n    Unit('km/h')\n\n### Instantiating quantities\n\nThe simplest way to create an instance of a class _Quantity_ subclass is to\ncall the class giving an amount and a unit. If the unit is omitted, the\nquantity's reference unit is used (if one is defined):\n\n    >>> Length(15, MILLIMETRE)\n    Length(Decimal(15), Unit('mm'))\n\nAlternatively, an amount and a unit can be multiplied:\n\n    >>> 17.5 * KILOMETRE\n    Length(Decimal('17.5'), Unit('km'))\n\nAlso, it's possible to create a _Quantity_ sub-class instance from a string\nrepresentation:\n\n    >>> Length('17.5 km')\n    Length(Decimal('17.5'), Unit('km'))\n\n### Unit-safe computations\n\nA quantity can be converted to a quantity using a different unit by calling\nthe method _Quantity.convert_:\n\n    >>> l5cm = Length(Decimal(5), CENTIMETRE)\n    >>> l5cm.convert(MILLIMETRE)\n    Length(Decimal(50), Unit('mm'))\n    >>> l5cm.convert(KILOMETRE)\n    Length(Decimal('0.00005'), Unit('km'))\n\nQuantities can be compared to other quantities using all comparison operators\ndefined for numbers. Different units are taken into account automatically, as\nlong as they are compatible, i.e. a conversion is available:\n\n    >>> Length(27) <= Length(91)\n    True\n    >>> Length(27, METRE) <= Length(91, CENTIMETRE)\n    False\n\nQuantities can be added to or subtracted from other quantities \u2026:\n\n    >>> Length(27) + Length(9)\n    Length(Decimal(36))\n    >>> Length(27) - Length(91)\n    Length(Decimal(-64))\n    >>> Length(27) + Length(12, CENTIMETER)\n    Length(Decimal('27.12'))\n    >>> Length(12, CENTIMETER) + Length(17, METER)\n    Length(Decimal('1712'), Length.Unit('cm'))\n\n\u2026 as long as they are instances of the same quantity type:\n\n    >>> Length(27) + Duration(9)\n    Traceback (most recent call last):\n    IncompatibleUnitsError: Can't add a 'Length' and a 'Duration'\n\nQuantities can be multiplied or divided by scalars, preserving the unit:\n\n    >>> 7.5 * Length(3, CENTIMETRE)\n    Length(Decimal('22.5'), Unit('cm'))\n    >>> Duration(66, MINUTE) / 11\n    Duration(Decimal(6), Unit('min'))\n\nQuantities can be multiplied or divided by other quantities \u2026:\n\n    >>> Length(15, METRE) / Duration(3, SECOND)\n    Velocity(Decimal(5))\n\n\u2026 as long as the resulting type of quantity is defined \u2026:\n\n    >>> Duration(4, SECOND) * Length(7)\n    Traceback (most recent call last):\n    UndefinedResultError: Undefined result: Duration * Length\n\n\u2026 or the result is a scalar:\n\n    >>> Duration(2, MINUTE) / Duration(50, SECOND)\n    Decimal('2.4')\n\n### Money\n\n_Money_ is a special type of quantity. Its unit type is known as currency.\n\nMoney differs from physical quantities mainly in two aspects:\n\n* Money amounts are discrete. For each currency there is a smallest fraction\n  that can not be split further.\n\n* The relation between different currencies is not fixed, instead, it varies\n  over time.\n\nThe sub-package _quantity.money_ provides classes and functions to deal\nwith these specifics.\n\nA currency must explicitly be registered as a unit for further use. The\neasiest way to do this is to call _Money.register_currency_. The method\nis backed by a database of currencies defined in ISO 4217. It takes the \n3-character ISO 4217 code as parameter.\n\n_Money_ derives from _Quantity_, so all operations on quantities can also be\napplied to instances of _Money_. But because there is no fixed relation\nbetween currencies, there is no implicit conversion between money amounts of\ndifferent currencies. Resulting values are always quantized to the smallest\nfraction defined with the currency.\n\nA conversion factor between two currencies can be defined by using the\nclass _ExchangeRate_. It is given a unit currency (aka base currency), a unit\nmultiple, a term currency (aka price currency) and a term amount, i.e. the\namount in term currency equivalent to unit multiple in unit currency.\n\nMultiplying an amount in some currency with an exchange rate with the same\ncurrency as unit currency results in the equivalent amount in term currency.\nLikewise, dividing an amount in some currency with an exchange rate with the\nsame currency as term currency results in the equivalent amount in unit\ncurrency.\n\nAs _Money_ derives from _Quantity_, it can be combined with other quantities\nin order to define a new quantity. This is, for example, useful for defining\nprices per quantum.\n\nFor more details see the documentation provided with the source distribution\nor [here](https://quantity.readthedocs.io/).\n",
    "bugtrack_url": null,
    "license": "BSD",
    "summary": "Unit-safe computations with quantities (including money)",
    "version": "0.11.4",
    "split_keywords": [
        "quantity",
        "quantities",
        "unit",
        "units",
        "money",
        "currency",
        "exchange"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3e04314e1c315ed491c237c30fcebee466b2a3a4a0d86a4d4caf8f6935429dd5",
                "md5": "fb43fb10f5e0d425b6a416bc3b7e9458",
                "sha256": "2d3273d5ac4845b369c8e063ee3c3a11c4ec1491e902e5b6ba78bb56570f57e0"
            },
            "downloads": -1,
            "filename": "quantity-0.11.4.tar.gz",
            "has_sig": false,
            "md5_digest": "fb43fb10f5e0d425b6a416bc3b7e9458",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 79941,
            "upload_time": "2023-01-20T16:25:27",
            "upload_time_iso_8601": "2023-01-20T16:25:27.794562Z",
            "url": "https://files.pythonhosted.org/packages/3e/04/314e1c315ed491c237c30fcebee466b2a3a4a0d86a4d4caf8f6935429dd5/quantity-0.11.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-20 16:25:27",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "mamrhein",
    "github_project": "quantity",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "quantity"
}
        
Elapsed time: 0.06560s