1

我想验证 a dict,其中的值符合以下规则:

  • 值必须是单个floatList(float)
  • 如果是 single float,则值必须为 1
  • 如果是 a List(float),每个浮点数必须是正数

这是我的代码和一些测试断言,它们工作正常:

import cerberus

v = cerberus.Validator()

schema1 = {
    "key1": {
        "type": ["float", "list"],
        "min": 1,
        "max": 1,
        "schema": {"type": "float", "min": 0},
    }
}
document1 = {"key1": 1}
document2 = {"key1": 5}
document3 = {"key1": "5"}
document4 = {"key1": [0.5, 0.3]}
document5 = {"key1": ["0.5", 0.3]}

assert v.validate(document1, schema1)
assert not v.validate(document2, schema1)
assert not v.validate(document3, schema1)
assert v.validate(document4, schema1)
assert not v.validate(document5, schema1)

现在,我必须再实现一个条件:

  • 如果是 a List(float),则sumoffloat必须等于 1

因此,我编写了文档( https://docs.python-cerberus.org/en/stable/validation-rules.htmlcheck_with中描述的函数。

from cerberus import Validator

class MyValidator(Validator):
    def _check_with_sum_eq_one(self, field, value):
        """Checks if sum equals 1"""

        if sum(value) != 1:
            self._error(field, f"Sum of '{field}' must exactly equal 1")

调整后的架构和测试文档如下所示:

v = MyValidator()

schema2 = {
    "key1": {
        "type": ["float", "list"],
        "min": 1,
        "max": 1,
        "schema": {"type": "float", "min": 0, "max": 1, "check_with": "sum_eq_one"},
    }
}

document1 = {"key1": 1}
document2 = {"key1": 5}
document3 = {"key1": "5"}
document4 = {"key1": [0.5, 0.3]}  # error
document5 = {"key1": ["0.5", 0.3]}  # error
document6 = {"key1": [0.5, 0.5]}  # error

现在,只要值是 a List(float),只有第一个元素list会被注入我的函数,导致 a TypeError: 'float' object is not iterable
验证时document4field将是int=0value=0.5。所以错误信息是有道理的。

我想知道,为什么整个列表没有传递给我的函数?我在这里想念什么?

4

2 回答 2

1

What if you try to catch the error and only continue your function, if the error was occurred? For example like in this manner:

class MyValidator(Validator):

def _check_with_sum_eq_one(self, field, value):
    """ Checks whether value is a list and its sum equals 1.0. """
    if isinstance(value, list) and sum(value) != 1.0:
        self._error(str(value), f"Sum of '{field}' must exactly equal 1")


schema2 = {
    "key1": {
        "type": ["list", "float"],
        "min": 1,
        "max": 1,
        "schema": {"type": "float", "min": 0, "max": 1},
        "check_with": "sum_eq_one",
    }
}

v = MyValidator(schema2)

document1 = {"key1": 1}
document2 = {"key1": 5}
document3 = {"key1": "5"}
document4 = {"key1": [0.3, 0.5]}  # error
document5 = {"key1": ["0.5", 0.3]}  # error
#document6 = {"key1": [0.5, 0.5]}  # error
assert v.validate(document1)
assert not v.validate(document2)
assert not v.validate(document3)
assert v.validate(document4)
assert not v.validate(document5)
于 2020-05-13T11:55:18.247 回答
0

以下答案工作正常。但是,在我看来,它过于复杂。

首先,调整schema2如下:

schema2 = {
    "key1": {
        "type": ["float", "list"],
        "min": 0,
        "max": 1,
        "check_with": "sum_eq_one"
    }
}

接下来调整_check_with_sum_eq_one如下:

class MyValidator(Validator):
    def _check_with_sum_eq_one(self, field, value):
        """Checks if sum equals 1"""

        if (isinstance(value, float) or isinstance(value, int)) and value != 1:
            self._error(field, f"Sum of '{field}' must exactly equal 1")
        if isinstance(value, list):
            if all([isinstance(x, float) for x in value]):
                if sum(value) != 1:
                    self._error(field, f"Sum of '{field}' must exactly equal 1")
            else:
                self._error(field, f"All list members must be of type ['float']")

最后,断言一切都按预期工作。

v = MyValidator()

document1 = {"key1": 1}
document2 = {"key1": 5}
document3 = {"key1": "5"}
document4 = {"key1": [0.5, 0.3]}
document5 = {"key1": ["0.5", 0.3]}
document6 = {"key1": [0.5, 0.5]}

assert v.validate(document1, schema2)
assert not v.validate(document2, schema2)
assert not v.validate(document3, schema2)
assert not v.validate(document4, schema2)
assert not v.validate(document5, schema2)
assert v.validate(document6, schema2)

我不喜欢这里的事实是,如果所有列表成员都是float( if all([isinstance(x, float) for x in value])) 类型,我需要“手动”检查。在我看来,这个测试属于schema2. 但是,我没有成功地进行调整schema2,即类型测试float先于check_with验证。

任何进一步简化此任务的提示将不胜感激。

于 2020-05-11T14:33:48.353 回答