Проверьте, равны ли словари python, допуская небольшую разницу для чисел с плавающей точкой - PullRequest
4 голосов
/ 08 мая 2019

Для словарей без чисел с плавающей запятой мы используем простые a == b, где a и b - словари python.Это работает хорошо, пока мы не получим a и b, содержащие числа с плавающей точкой где-то внутри.Это вложенные словари, поэтому я думаю, что это доставляет pytest.approx неприятностей.

То, что мы хотим, - это то, что скажет нам, что эти два словаря равны (или приблизительно равны, но что-то, что не потерпит неудачу только наприближения с плавающей запятой):

{"foo": {"bar": 0.30000001}} == {"foo": {"bar": 0.30000002}}

pytest.approx() - это почти , что я хочу, но он не поддерживает вложенные словари.Есть ли что-то, что может делать то, что я хочу?

Ответы [ 3 ]

2 голосов
/ 08 мая 2019

Вы можете определить свой собственный помощник аппроксимации с поддержкой вложенных словарей. К сожалению, pytest не поддерживает расширение approx с помощью пользовательских компараторов, поэтому вы должны написать свою собственную функцию; однако, это не было слишком сложно:

import pytest
from _pytest.compat import Mapping
from _pytest.python_api import ApproxMapping


def my_approx(expected, rel=None, abs=None, nan_ok=False):
    if isinstance(expected, Mapping):
        return ApproxNestedMapping(expected, rel, abs, nan_ok)
    return pytest.approx(expected, rel, abs, nan_ok)


class ApproxNestedMapping(ApproxMapping):
    def _yield_comparisons(self, actual):
        for k in self.expected.keys():
            if isinstance(actual[k], type(self.expected)):
                gen = ApproxNestedMapping(
                    self.expected[k], rel=self.rel, abs=self.abs, nan_ok=self.nan_ok
                )._yield_comparisons(actual[k])
                for el in gen:
                    yield el
            else:
                yield actual[k], self.expected[k]

    def _check_type(self):
        for key, value in self.expected.items():
            if not isinstance(value, type(self.expected)):
                super()._check_type()

Теперь используйте my_approx вместо pytest.approx:

def test_nested():
    assert {'foo': {'bar': 0.30000001}} == my_approx({'foo': {'bar': 0.30000002}})
1 голос
/ 08 мая 2019

Что вы можете сделать, это выделить значения внутри словарей и проверить, меньше ли абсолютное значение разницы между значениями, чем когда-либо, что делает его «Достаточно близко».Я нашел здесь функцию , которая используется для распаковки вложенных словарей.

epislon = 5 

def extract_nested_values(it):
    if isinstance(it, list):
        for sub_it in it:
            yield from extract_nested_values(sub_it)
    elif isinstance(it, dict):
        for value in it.values():
            yield from extract_nested_values(value)
    else:
        yield it


d = {"foo": {"bar": 0.30000001}}
#[0.30000001]
e = {"foo": {"bar": 0.30000002}}
#[0.30000002]

d_value = list(extract_nested_values(d))
e_value = list(extract_nested_values(e))

if set(d.keys()) == set(e.keys()) and abs(e_value[0] - d_value[0]) < epislon:
    print('Close Enough')
else:
    print("not the same")

Выход:

Close Enough
0 голосов
/ 08 мая 2019

Задумывались ли вы о копировании словарей (чтобы не влиять на исходные значения), итерации по каждому значению и округлении каждого числа с плавающей запятой round()?

math.isclose() такжесравнивает числа с плавающей точкой, но я не знаю ни одной, которая сравнивает все числа с плавающей точкой во вложенных словарях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...