You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python序列化器批量设置Decimal上下文精度?

问题描述

我编写了如下Python序列化器代码:

from decimal import Decimal, setcontext, getcontext

class MyNewSerializer(serializers.Serializer):
    total_value_base = serializers.SerializerMethodField()
    total_outgoing_value_base = serializers.DecimalField(
        max_digits=38,
        decimal_places=8,
        source="value_sent",
    )
    total_incoming_value = serializers.DecimalField(
        max_digits=38,
        decimal_places=4,
        source="value_received",
    )

    def get_total_value_base(self, obj):
        total = Decimal(obj.value_received) + Decimal(
            obj.value_sent
        )
        # Values of above objects
        # obj.value_received = 425933085766969760747388.45622168 
        # obj.value_sent = 0
        # total = 425933085766969760747388.4562  

        dec = Decimal(str(total))
        return round(dec, 8)

运行时抛出错误:

return round(dec, 8)
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

添加getcontext().prec = 100后问题解决:

def get_total_value_base(self, obj):
        # the below line fixes the issue
        getcontext().prec = 100
        
        total = Decimal(obj.value_received) + Decimal(
            obj.value_sent
        )
        # Values of above objects
        # obj.value_received = 425933085766969760747388.45622168 
        # obj.value_sent = 0
        # total = 425933085766969760747388.4562  

        dec = Decimal(str(total))
        return round(dec, 8)

现在需要为当前类及其他同类序列化器中的所有Decimal值提升精度,涉及文件中多个类的上百个变量。请问如何通过基类或重写Decimal类实现全局/批量设置?


解决方案

以下是几种批量提升Decimal精度的可行方案:

1. 自定义基础序列化器基类

创建一个基类,在初始化时设置Decimal上下文精度,所有需要提升精度的序列化器都继承这个基类:

from decimal import getcontext
from rest_framework import serializers

class HighPrecisionSerializer(serializers.Serializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 设置全局Decimal精度,覆盖默认的28位
        getcontext().prec = 100

# 原有序列化器继承这个基类即可
class MyNewSerializer(HighPrecisionSerializer):
    total_value_base = serializers.SerializerMethodField()
    total_outgoing_value_base = serializers.DecimalField(
        max_digits=38,
        decimal_places=8,
        source="value_sent",
    )
    # ... 其他字段保持不变

    def get_total_value_base(self, obj):
        total = Decimal(obj.value_received) + Decimal(obj.value_sent)
        dec = Decimal(str(total))
        return round(dec, 8)

所有继承HighPrecisionSerializer的类都会自动应用高精度设置,无需在每个方法中重复编写精度配置代码。

2. 自定义高精度DecimalField

如果只需要针对DecimalField字段提升精度,可以自定义一个HighPrecisionDecimalField替换原有字段:

from decimal import getcontext
from rest_framework import serializers

class HighPrecisionDecimalField(serializers.DecimalField):
    def to_internal_value(self, data):
        # 转换前设置精度
        getcontext().prec = 100
        return super().to_internal_value(data)
    
    def to_representation(self, value):
        # 序列化输出时保持高精度
        getcontext().prec = 100
        return super().to_representation(value)

# 使用自定义字段替换原有DecimalField
class MyNewSerializer(serializers.Serializer):
    total_value_base = serializers.SerializerMethodField()
    total_outgoing_value_base = HighPrecisionDecimalField(
        max_digits=38,
        decimal_places=8,
        source="value_sent",
    )
    total_incoming_value = HighPrecisionDecimalField(
        max_digits=38,
        decimal_places=4,
        source="value_received",
    )

    # ... 其他方法不变

这种方式精准控制所有Decimal字段的精度处理,适合无需全局修改Serializer、仅针对字段调整的场景。

3. 全局设置Decimal上下文

如果希望整个项目的Decimal操作都使用高精度,可在项目启动时(如settings.py或入口文件)设置全局精度:

# 在项目初始化文件中添加
from decimal import getcontext
getcontext().prec = 100

该方式最简单,一次性设置后所有Decimal相关操作都会使用100位精度,无需修改任何序列化器代码。注意此设置会影响项目中所有Decimal运算,需确认不会引发其他问题。

4. 自定义Decimal子类(可选)

若需要更细粒度的控制,可自定义高精度Decimal子类,在序列化器中替代原生Decimal:

from decimal import Decimal, getcontext

class HighPrecisionDecimal(Decimal):
    def __new__(cls, value="0", context=None):
        # 强制使用高精度上下文
        ctx = getcontext()
        original_prec = ctx.prec
        ctx.prec = 100
        instance = super().__new__(cls, value, context=ctx)
        ctx.prec = original_prec  # 可选:恢复原有精度,避免影响其他代码
        return instance

# 在SerializerMethodField中使用自定义子类
def get_total_value_base(self, obj):
    total = HighPrecisionDecimal(obj.value_received) + HighPrecisionDecimal(obj.value_sent)
    dec = HighPrecisionDecimal(str(total))
    return round(dec, 8)

此方式适合单独控制某些Decimal实例精度的场景,但替换上百个变量时,效率不如前几种方案。


内容的提问来源于stack exchange,提问作者Cool Breeze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:37:11