用于浮点集合的Python单元测试中的assertAlmostEqual

sna*_*ile 61 python collections unit-testing

Python的单元测试框架中assertAlmostEqual(x,y)方法测试是否和大致相等,假设它们是浮点数.xy

问题assertAlmostEqual()是它只适用于浮点数.我正在寻找一种方法,如assertAlmostEqual()浮点数,浮点数集,浮点字典,浮点元组,浮点元组列表,浮点列表等等.

例如,让x = 0.1234567890,y = 0.1234567891.x并且y几乎相同,因为他们同意除最后一个数字之外的每个数字.因此self.assertAlmostEqual(x, y)True因为assertAlmostEqual()花车的作品.

我正在寻找一个更通用的assertAlmostEquals(),也评估以下调用True:

  • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).
  • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).
  • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).

有这样的方法还是我必须自己实施?

澄清:

  • assertAlmostEquals()有一个名为的可选参数places,通过计算舍入到十进制数的差值来比较数字places.默认情况下places=7,因此self.assertAlmostEqual(0.5, 0.4)为False,而self.assertAlmostEqual(0.12345678, 0.12345679)为True.我的推测assertAlmostEqual_generic()应该具有相同的功能.

  • 如果两个列表在完全相同的顺序中具有几乎相等的数字,则认为它们几乎相等.正式地,for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).

  • 类似地,如果可以将两个集合转换为几乎相等的列表(通过为每个集合分配顺序),则认为两个集合几乎相等.

  • 类似地,如果每个字典的密钥集几乎等于另一个字典的密钥集,则认为两个字典几乎相等,并且对于每个这样几乎相等的密钥对,存在相应的几乎相等的值.

  • 一般来说:我认为两个集合几乎相等,只要它们相等,除了一些相应的浮点数几乎相等.换句话说,我想比较对象,但在比较沿途的浮动时具有较低(自定义)精度.

Pie*_* GM 57

如果您不介意使用NumPy(随Python(x,y)一起提供),您可能需要查看np.testing定义assert_almost_equal函数的模块.

签名是 np.testing.assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True)

>>> x = 1.000001
>>> y = 1.000002
>>> np.testing.assert_almost_equal(x, y)
AssertionError: 
Arrays are not almost equal to 7 decimals
ACTUAL: 1.000001
DESIRED: 1.000002
>>> np.testing.assert_almost_equal(x, y, 5)
>>> np.testing.assert_almost_equal([x, x, x], [y, y, y], 5)
>>> np.testing.assert_almost_equal((x, x, x), (y, y, y), 5)
Run Code Online (Sandbox Code Playgroud)

  • 这很接近,但是`numpy.testing`几乎相同的方法仅适用于数字,数组,元组和列表.它们不适用于字典,集合和集合集合. (2认同)

sna*_*ile 9

以下是我实现泛型is_almost_equal(first, second)函数的方法:

首先,复制需要比较(firstsecond)的对象,但不要制作精确的副本:剪切在对象内遇到的任何浮点数的无效小数位.

现在,你有副本first,并second为这微不足道的小数位数都没有了,只是比较firstsecond使用的==运营商.

假设我们有一个cut_insignificant_digits_recursively(obj, places)重复的函数,obj但只留下places原始中每个浮点数的最高有效十进制数字obj.这是一个有效的实现is_almost_equals(first, second, places):

from insignificant_digit_cutter import cut_insignificant_digits_recursively

def is_almost_equal(first, second, places):
    '''returns True if first and second equal. 
    returns true if first and second aren't equal but have exactly the same
    structure and values except for a bunch of floats which are just almost
    equal (floats are almost equal if they're equal when we consider only the
    [places] most significant digits of each).'''
    if first == second: return True
    cut_first = cut_insignificant_digits_recursively(first, places)
    cut_second = cut_insignificant_digits_recursively(second, places)
    return cut_first == cut_second
Run Code Online (Sandbox Code Playgroud)

这是一个有效的实施方案cut_insignificant_digits_recursively(obj, places):

def cut_insignificant_digits(number, places):
    '''cut the least significant decimal digits of a number, 
    leave only [places] decimal digits'''
    if  type(number) != float: return number
    number_as_str = str(number)
    end_of_number = number_as_str.find('.')+places+1
    if end_of_number > len(number_as_str): return number
    return float(number_as_str[:end_of_number])

def cut_insignificant_digits_lazy(iterable, places):
    for obj in iterable:
        yield cut_insignificant_digits_recursively(obj, places)

def cut_insignificant_digits_recursively(obj, places):
    '''return a copy of obj except that every float loses its least significant 
    decimal digits remaining only [places] decimal digits'''
    t = type(obj)
    if t == float: return cut_insignificant_digits(obj, places)
    if t in (list, tuple, set):
        return t(cut_insignificant_digits_lazy(obj, places))
    if t == dict:
        return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
    return obj
Run Code Online (Sandbox Code Playgroud)

代码及其单元测试可在此处获得:https://github.com/snakile/approximate_comparator.我欢迎任何改进和错误修复.

  • @Jérôme,感谢您的评论.我刚刚添加了MIT许可证. (2认同)

小智 7

从python 3.5开始,你可以比较使用

math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)
Run Code Online (Sandbox Code Playgroud)

pep-0485中所述.实施应该相当于

abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )
Run Code Online (Sandbox Code Playgroud)

  • 这如何帮助比较问题所问的带有浮子的容器? (4认同)

DJC*_*ley 5

如果你不介意使用numpy包,那么numpy.testingassert_array_almost_equal方法。

这适用于array_like对象,因此适用于浮点数的数组,列表和元组,但不适用于集合和字典。

文档在这里