【问题标题】:DynamoDBNumberError on trying to insert floating point number using python boto libraryDynamoDBNumberError 尝试使用 python boto 库插入浮点数
【发布时间】:2022-04-22 14:59:03
【问题描述】:

代码sn-p:

conn = dynamo_connect()

company = Table("companydb",connection=conn)

companyrecord = {'company-slug':'www-google-com12','founding-year':1991, 'randomlist' :[1,2,3,4,5], 'randomdict' : {'a':[1,2,3],'b':'something','randomnumber':10.55} }

company.put_item(data=companyrecord)

我收到以下错误:

File "C:\Python27\lib\site-packages\boto\dynamodb2\items.py", line 329, in prepare_full
    final_data[key] = self._dynamizer.encode(value)
  File "C:\Python27\lib\site-packages\boto\dynamodb\types.py", line 279, in encode
    return {dynamodb_type: encoder(attr)}
  File "C:\Python27\lib\site-packages\boto\dynamodb\types.py", line 335, in _encode_m
    return dict([(k, self.encode(v)) for k, v in attr.items()])
  File "C:\Python27\lib\site-packages\boto\dynamodb\types.py", line 279, in encode
    return {dynamodb_type: encoder(attr)}
  File "C:\Python27\lib\site-packages\boto\dynamodb\types.py", line 305, in _encode_n
    raise DynamoDBNumberError(msg)
boto.dynamodb.exceptions.DynamoDBNumberError: BotoClientError: Inexact numeric for `10.55`

【问题讨论】:

  • 介意包括实际导致错误的代码...?
  • @tzaman 我已经更新了上面帖子中的代码。我尝试为没有浮点值的字典插入记录,插入工作得很好。即使将 10.55 设置为 '10.55' ,它也可以工作,但 Dynamodb 数据类型随后被解释为 String 而不是 Number

标签: python amazon-web-services floating-point amazon-dynamodb boto


【解决方案1】:

如果您正在处理更大的集合并且希望避免按记录处理来转换十进制,这会有所帮助。

from decimal import Decimal

import json

changed_data = json.loads(json.dumps(data), parse_float=Decimal)

【讨论】:

  • json 包不处理集合,顺便说一句。
【解决方案2】:

是的,GitHub 上有已知的issues 与浮点数相关,可能有两种解决方法,首先如果您愿意存储10.5 而不是10.55,那么我猜它会正常工作,另一个是将浮点值存储为字符串或整数,然后在访问时对其进行调制。

所以你们选择了字符串部分,然后您可以将其存储为'10.55' 而不是10.55,然后当您从表中访问值时,您可以简单地使用float("10.55"),然后就完成了。

另一种方法是将其存储为整数,首先选择一个精度值(例如 2 个十进制值)然后将 10.55 存储为 1055(乘以 100,因为 2 个十进制精度),并在访问它时你可以简单地使用1055/100.0,你会得到10.55

【讨论】:

  • 这不是解决问题的方法,它只是一个小技巧。
  • 真的。我不想丢失任何精度数据——这让我有了选项 2——将 python 字典中的所有浮点值转换为字符串并在调用 put-item 之前重新构建字典,这样我就可以清除 boto 验证而不是抛出例外。另外,要记住的一件事是字典可以嵌套,它必须在任何一种情况下都可以工作。您知道任何可以执行此功能的库吗?
  • 没错,所以尝试使用string而不是float
  • 有道理,我编写了自己的包装器来将所有浮点数转换为字符串值。感谢@anmol_uppal 的帮助。
【解决方案3】:

使用 Decimal(str(your_number)) 代替。 见https://github.com/boto/boto3/issues/665

【讨论】:

    【解决方案4】:

    这是我通过覆盖 typeSerializer 插入浮点数的解决方案。

    from boto3.dynamodb import types
    from decimal import Decimal, localcontext
    import re
    
    class CustomSerializer(types.TypeSerializer):
    
        def _is_number(self, value):
            if isinstance(value, (int, Decimal, float)):
                return True
          return False
    
    # Add float-specific serialization code
    def _serialize_n(self, value):
        if isinstance(value, float):
            with localcontext(types.DYNAMODB_CONTEXT) as context:
                stringify_val = str(value)
                number = str(context.create_decimal(stringify_val))
                return number
    
        number = super(CustomSerializer, self)._serialize_n(value)
        return number
    
    serializer = CustomSerializer()
    serialized_data = serializer.serialize(data)
    

    【讨论】:

      【解决方案5】:

      Python3 提供float.hex() / .fromhex() 将浮点数存储为字符串而不会丢失精度:

      两种方法支持与十六进制字符串的转换。由于 Python 的浮点数在内部存储为二进制数,因此将浮点数转换为十进制字符串或从十进制字符串转换通常会涉及一个小的舍入误差。相反,十六进制字符串允许精确表示和指定浮点数。这在调试和数值工作时很有用。

      如果您不想降低任何精度,这可能是@ZdaR 使用str()float() 进行转换的解决方案的替代方案。

      [注意:我是新用户,无法评论ZdaR's solution]

      【讨论】:

        【解决方案6】:

        以防万一它适用于某人。

        我有这样的结构:

        {
            "hash_key": 1,
            "range_key": 2,
            "values": {
                "valueA": 1.2,
                "valueB": 2.1,
                "valueC": 3.1,
                "valueD": 4.1,
                "valueE": 5.1,
                "valueF": 6.1
            }
        }
        

        我在一个名为 parameters 的对象中拥有这本字典 额外说明;我进行了此验证,因为并非所有时候我都收到 values 键:

        values = parameters['values'] if 'values' in parameters else {}
        
        if values: # So... this made the trick:
            parameters['values'] = [{key: str(value)} for key, value in values.items()]
        

        这种转变成功了。

        我也在用

        boto3.resource('dynamodb')
        table = dynamo.Table(TABLE_NAME)
        table.put_item(Item=parameters, ReturnValues='ALL_OLD')
        

        完整的函数如下所示:

        def create_item(parameters):
            dynamo = boto3.resource('dynamodb')
            table = dynamo.Table('your_table_name')
        
            parameters['hash_key'] = str(parameters['hash_key'])
            parameters['range_key'] = str(parameters['range_key'])
            values = parameters['values'] if 'values' in parameters else {}
        
            if values:
                parameters['values'] = [{key: str(value)} for key, value in values.items()]
        
            try:
                response = table.put_item(Item=parameters, ReturnValues='ALL_OLD')
            except ClientError as e:
                response = e.response['Error']
                # this is a local function to give format to the error
                return create_response(response['Message'], response)
            return response
        

        不确定这是否是 tldr 案例,希望对 c:

        有所帮助

        【讨论】:

          【解决方案7】:

          您需要将浮点值解析为十进制

          from datetime import datetime
          from decimal import Decimal
          
          decimal_value = Decimal(datetime.utcnow().timestamp())
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-05-19
            • 2011-06-06
            • 2021-03-25
            • 2023-01-07
            • 2021-05-28
            • 1970-01-01
            • 2020-07-16
            相关资源
            最近更新 更多