1

这将是我在这里的第一个问题。我正在尝试创建一个 decimal.Decimal 子类,该子类主要通过对其自身及其返回 Decimal 对象的可调用参数的结果进行自动规范化来区分父类。下面的代码具有装饰 Decimal 的所有方法以返回 MyDecimal 实例(通过创建其字符串末尾的零)而不是 decimal.Decimals 的概念。为此,使用了元类。

但是,我觉得这段代码有点hacky。此外,根据速度测试结果,它也非常慢:在我的系统上,十进制为 2.5 秒。十进制与 MyDecimal 为 16 秒。

我的问题是:有没有更清洁(更快)的方式来做到这一点?

import decimal

class AutoNormalizedDecimal(type):
    def __new__(cls, name, bases, local):
        local_items = list(local.items())
        parent_items = [i for i in bases[0].__dict__.items()
            if i[0] not in local.keys()]
        for a in local_items + parent_items:
            attr_name, attr_value = a[0], a[1]
            if callable(attr_value):
                local[attr_name] = cls.decorator(attr_value)
        return super(AutoNormalizedDecimal, cls).__new__(
            cls, name, bases, local)

    @classmethod
    def decorator(cls, func):
        def wrapper_for_new(*args, **kwargs):
            new_string = args[1].rstrip('0').rstrip('.')
            if not new_string:
                new_string = '0'
            newargs = (args[0], new_string)
            return func(*newargs, **kwargs)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            if (isinstance(result, decimal.Decimal)
                and not isinstance(result, MyDecimal)):
                return MyDecimal(str(result))
            return result
        if func.__name__ == '__new__':
            return wrapper_for_new
        return wrapper

class MyDecimal(decimal.Decimal, metaclass=AutoNormalizedDecimal):
    def __str__(self):
        return decimal.Decimal.__str__(self).replace('.', ',') 

n = MyDecimal('-5423.5420000')

def speedtest():
    import time
    start = time.time()
    d = decimal.Decimal('6')
    for i in range(1000000):
        d += decimal.Decimal(str(i))
    print(time.time()-start)

    start = time.time()
    d = MyDecimal('6')
    for i in range(1000000):
        d += MyDecimal(str(i))
    print(time.time()-start)

这是它的工作原理:

>>> n
Decimal('-5423.542')
>>> type(n)
<class '__main__.MyDecimal'>
>>> str(n)
'-5423,542'
>>> x = MyDecimal('542.63') / MyDecimal('5.2331')
>>> x
Decimal('103.6918843515315969501824922')
>>> type(x)
<class '__main__.MyDecimal'>
>>> y = MyDecimal('5.5252') - MyDecimal('0.0052')
>>> y
Decimal('5.52')
>>> z = decimal.Decimal('5.5252') - decimal.Decimal('0.0052')
>>> z
Decimal('5.5200')

提前致谢!

PS:感谢 Anurag Uniyal 的代码,这给了我一个开始的方式:https ://stackoverflow.com/a/3468410/2334951

EDIT1:我出来重新定义 as_tuple() 方法,我可以一直调用它,我需要修剪的十进制版本:

class MyDecimal(decimal.Decimal):

    def as_tuple(self):
        sign, digits_, exponent = super().as_tuple()
        digits = list(digits_)
        while exponent < 0 and digits[-1] == 0:
            digits.pop()
            exponent += 1
        while len(digits) <= abs(exponent):
            digits.insert(0, 0)
        return decimal.DecimalTuple(sign, tuple(digits), exponent)

    def __str__(self):
        as_tuple = self.as_tuple()
        left = ''.join([str(d) for d in as_tuple[1][:as_tuple[2]]])
        right = ''.join([str(d) for d in as_tuple[1][as_tuple[2]:]])
        return ','.join((left, right))
4

0 回答 0