在Python字典中断言所有值的数据类型的更好方法

2024-06-08 21:11:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在构建一个单元测试,它断言/检查字典中的所有值是否具有相同的数据类型:float

Python版本3.7.4

假设我有四本不同的字典:

dictionary1: dict = {
    "key 1": 1.0,
}

dictionary2: dict = {
    "key 1": "1.0",
}

dictionary3: dict = {
    "key 1": 1.0,
    "key 2": 2.0,
    "key 3": 3.0
}

dictionary4: dict = {
    "key 1": "1",
    "key 2": "2",
    "key 3": 3
}

以及单元测试用例:

class AssertTypeUnitTest(unittest.TestCase):

    def test_value_types(self):
        dictionary: dict = dictionary
        self.assertTrue(len(list(map(type, (dictionary[key] for key in dictionary)))) is 1 and
                            list(map(type, (dictionary[key] for key in dictionary)))[0] is float)


if __name__ == "__main__":
    unittest.main()

预期的结果是,如果字典中有一个值不是float,它将抛出一个AssertionError,即它将对dictionary2而不是dictionary1执行该操作

现在,虽然测试对1个键值对有效,但在dictionary3dictionary4等情况下,如何对多个键值对有效,而不必添加另一个for循环

for type in list(map(type, (dictionary[key] for key in dictionary))):
    self.assertTrue(type is float)

谢谢


Tags: keyinselfmapfordictionary字典is
3条回答

我有一个类似的问题,但希望使用python标准库的typing模块以更通用的方式解决它。它使用包pydantic,这似乎是一个合理的依赖项。这是我的方法

import unittest
import typing
import pydantic

def check_type(obj, expected_type):

    class Model(pydantic.BaseModel):
        data: expected_type

    # convert ValidationError to TypeError if the obj does not match the expected type
    try:
        Model(data=obj)
    except pydantic.ValidationError as ve:
        raise TypeError(str(ve.errors()))

    return True  # allow constructs like assert check_type(x, List[float])

class TestCase1(unittest.TestCase):

    def test_check_type(self):

        obj1 = [3, 4, 5]
        obj2 = [3, 4, "x"]

        check_type(obj1, typing.List[int]) # pass silently

        with self.assertRaises(TypeError) as cm:
            check_type(obj2, typing.List[int])

        obj3 = {
                "key 1": 1.0,
                "key 2": 2.0,
                "key 3": 3.0
                }

        check_type(obj3, typing.Dict[str, pydantic.StrictFloat])

        obj3["key 3"] = "3.0"

        with self.assertRaises(TypeError) as cm:
            check_type(obj3, typing.Dict[str, pydantic.StrictFloat])

        # note that this passes:
        check_type(obj3, typing.Dict[str, float])


        # allow for multiple types:
        check_type(obj3, typing.Dict[str, typing.Union[int, float, str]])

让我们打破您编写的断言,了解它实际上在做什么

self.assertTrue(
    len(list(map(type, (dictionary[key] for key in dictionary)))) is 1  #
    and  #
    list(map(type, (dictionary[key] for key in dictionary)))[0] is float
)

这里有两个条件必须为true,断言才能通过。第一个是

len(list(map(type, (dictionary[key] for key in dictionary)))) is 1

这将创建一个包含字典中每个值类型的列表,计算其长度并检查是否为一个。好吧,这个列表将始终具有与字典相同的元素数。检查此长度是否等于1与您的要求无关

第二个条件是

list(map(type, (dictionary[key] for key in dictionary)))[0] is float

这将创建一个具有字典中每个值类型的列表,如前所述,然后检查该列表的第一个元素是否为float。这并不意味着原始字典中的所有元素都是浮动的

使用您已经编写的内容,检查的简单方法是获取类型列表,获取第一个元素,然后检查所有元素是否等于第一个元素

l = list(map(type, (dictionary[key] for key in dictionary)))
for t in l: 
    self.assertTrue(t==l[0])

但是有很多方法可以进行测试。您还可以将此列表转换为一个集合,并检查其长度是否等于一(因为集合将只保留唯一值)

self.assertTrue(len(set(l)) == 1)
# or self.assertEqual(len(set(l)), 1)

您可以将项目类型转换为一个集合,并断言它等于一个float集合:

self.assertSetEqual(set(map(type, dictionary.values())), {float})

相关问题 更多 >