Показаны сообщения с ярлыком decimal. Показать все сообщения
Показаны сообщения с ярлыком decimal. Показать все сообщения

4 мая 2014 г.

Range для не целых чисел

Почему-то никогда не думал, что range (и xrange) понимают только целые числа (в Python 3 ситуация такая же) и функцией нельзя воспользоваться, как например:

range(0., 5., .5)
range(Decimal('0'), Decimal('10'), Decimal('1.5'))

Поэтому пришлось сделать свою замену:

import operator


def arange(start, stop=None, step=None):
    """
    Implement range function not only for integers as Python's builtin
    function, but for Decimals and floats as well.

    Returns generator with arithmetic progession, not list.
    """
    klass = type(start)
    lt_func = operator.lt

    stop = start if stop is None else stop
    start = klass(0) if start == stop else start
    step = klass(1 if step is None else step)

    assert isinstance(stop, klass), (
        'Start and stop limits have different types, {0!r} != {1!r}.'.
        format(type(start).__name__, type(stop).__name__)
    )
    assert step, "Step shouldn't be a zero: {0!r}.".format(step)

    if start < stop and step < 0 or start > stop and step > 0:
        raise StopIteration
    elif start > stop and step < 0:
        lt_func = operator.gt

    while lt_func(start, stop):
        yield start
        start += step

В отличии от range в Python 2 arange отдает генератор, а не список, что вообщем-то удобней и правильней, ну и функция всегда вернет элементы такого же типа, как были переданы в start и stop аргументы, даже если шаг - это целое число или он не указан вовсе.

Как всегда код оформлен как Gist на GitHub'е, там еще доктесты и юниттесты.

3 ноября 2011 г.

randdecimal

Понадобилось написать функцию которая будет отдавать случайный Decimal между значениями a и b (аналогично для встроенной random.randint, только с поддержкой Decimal). Получилось все довольно просто:

from decimal import Decimal
from random import randint


def randdecimal(a, b):
    """
    Return a random decimal N such that a <= N <= b.
    """
    a, b = Decimal(a), Decimal(b)
    diff = b - a

    multiplier = pow(10, abs(diff.as_tuple()[2]))
    diff_to_int = int(diff * multiplier)

    return a + Decimal(randint(0, diff_to_int)) / multiplier

Результат работы:

In [3]: from randdecimal import randdecimal

In [4]: randdecimal(0, 10)
Out[4]: Decimal('5')

In [5]: randdecimal(0, 10)
Out[5]: Decimal('1')

In [6]: randdecimal(0, 10)
Out[6]: Decimal('1')

In [7]: randdecimal('0.13', '0.20')
Out[7]: Decimal('0.20')

In [8]: randdecimal('0.13', '0.20')
Out[8]: Decimal('0.14')

In [9]: randdecimal('0.13', '0.20')
Out[9]: Decimal('0.15')

In [10]: randdecimal('0.135', '0.2')
Out[10]: Decimal('0.139')

In [11]: randdecimal('0.135', '0.2')
Out[11]: Decimal('0.194')

In [12]: randdecimal('0.135', '0.2')
Out[12]: Decimal('0.172')

In [13]: randdecimal('0.135', '0.2')
Out[13]: Decimal('0.182')

In [14]: from decimal import Decimal

In [15]: randdecimal(Decimal('5.34'), 10)
Out[15]: Decimal('8.69')

In [16]: randdecimal(Decimal('5.34'), 10)
Out[16]: Decimal('9.17')

In [17]: randdecimal(Decimal('5.34'), 10)
Out[17]: Decimal('6.34')

In [18]: randdecimal(0, Decimal('5.33'))
Out[18]: Decimal('2.66')

In [19]: randdecimal(0, Decimal('5.33'))
Out[19]: Decimal('2')

In [20]: randdecimal(0, Decimal('5.33'))
Out[20]: Decimal('3.21')

Полностью randdecimal.py вместе с тестами доступен как gist на GitHub'е.