尝试使用 python boto 库插入浮点数时出现 DynamoDBNumberError

DynamoDBNumberError on trying to insert floating point number using python boto library

代码片段:

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`

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

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

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

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

Two methods support conversion to and from hexadecimal strings. Since Python’s floats are stored internally as binary numbers, converting a float to or from a decimal string usually involves a small rounding error. In contrast, hexadecimal strings allow exact representation and specification of floating-point numbers. This can be useful when debugging, and in numerical work.

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

[注意:我是新用户,无法对]发表评论

改用 Decimal(str(your_number))。 参见 https://github.com/boto/boto3/issues/665

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

from decimal import Decimal

import json

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

这是我通过覆盖 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)

以防万一它对某人有用。

我有这样的结构:

{
    "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 = 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:

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

from datetime import datetime
from decimal import Decimal

decimal_value = Decimal(datetime.utcnow().timestamp())